迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > Java >

Java Synchronised变量

作者:迹忆客 最近更新:2023/07/16 浏览次数:

本文将讨论如何在Java中同步或锁定变量。

同步或锁定在多线程编程中至关重要。 并行运行的线程可能会尝试访问相同的变量或其他资源,从而产生意外的结果。

同步或锁定是避免此类错误情况的解决方案。


synchronized 关键字

同步是Java中的传统方法。 同步块是使用 synchronized 关键字实现的。

一旦一个线程进入同步代码,其他Java线程就会处于阻塞状态。 当前Java线程退出同步块后,其他Java线程可以尝试访问同步块。

这种方法有一个缺点。 同步块不允许线程在当前Java线程完成工作后在队列中等待并访问变量,因此Java线程必须等待很长时间。

将 Synchronized 关键字与方法或块一起使用

在下面的示例中,我们在 MultiThreadList 类的 run() 方法中同步 MenuObj。 我们还可以使用synchronized 定义 listItem() 代码块。

两个函数都会给出相同的结果。

同一个程序可以采用如下的同步方法。

public synchronized void listItem(String item){
{/*Code to Synchronize*/}
}
synchronized(this)
    {
        System.out.println("\nMenu Item - \t"  + item);
        try{
            Thread.sleep(1000);
        }
        catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("Listed - " + item);
    }

在这里,用户可以看到使用同步块的完整示例代码。

示例代码:

import java.io.*;
import java.util.*;
//Menu class
class Menu
{
    //List an item
    public void listItem(String item)
    {
        System.out.println("\nMenu Item - "  + item);
        try {
            Thread.sleep(1000);
        }
        catch (Exception e){
              e.printStackTrace();
        }
        System.out.println("Listed - " + item);
    }
}
//Multi-thread menu listing
class MultiThreadList extends Thread
{
    private String item;
    Menu  MenuObj;
  /* Gets menu object and a string item*/
    MultiThreadList(String m,  Menu obj)
    {
        item=m;
        MenuObj=obj;
    }
    public void run() {
       /* Only one Java thread can list an item at a time.*/
        synchronized(MenuObj){
            //Menu object synchronized
            MenuObj.listItem(item);
        }}}
//Main
class MainSync
{
    public static void main(String args[])
    {
        Menu listItem=new Menu();
        MultiThreadList M1=
            new MultiThreadList( "Rice" , listItem );
        MultiThreadList M2=
            new MultiThreadList( "Icecream" , listItem );

        // Start two threads
        M1.start();
        M2.start();

        //Wait for thread completion
        try{
            M1.join();
            M2.join();
        }
        catch(Exception e){
            e.printStackTrace();
        }}}

输出:

Menu Item - Rice
Listed - Rice
Menu Item - Icecream
Listed - Icecream

在上面的输出中,用户可以观察到,当一个线程访问 MenuObj 时,其他线程无法访问,因为它会打印列出的项目,然后打印菜单项。 如果多个线程尝试同时访问同一块,则它应该以不同的顺序打印菜单项和列出的项目。


使用 ReentrantLock 方法

Java中的 ReentrantLock 类是一种灵活的锁定变量的方法。 ReentrantLock 类在访问公共资源或变量时提供同步。

lock()unlock() 方法执行该过程。 一次一个 Java 线程获取锁。 其他线程在此期间将处于阻塞状态。

线程可以多次进入锁。 因此,名称是可重入的。 当 Java 线程获得锁时,保持计数为 1,并且计数在重新进入时累加。

运行unlock() 后,锁定保持计数减一。 ReentrantLock 根据等待时间为线程提供服务。

等待时间较长的Java线程优先。

为了在出现某些异常时释放锁,lock() 在 try 块之前运行。 unblock() 在finally 块内运行。

ReentrantLock 函数

   
lock() 将计数递增一。 如果该变量是空闲的,则为 Java 线程分配一个锁。
unlock() 将计数减一。 当保持计数为零时锁定释放。
tryLock() 如果资源空闲,该函数返回true。 否则,线程退出。
lockInterruptically() 当一个 Java 线程使用锁时,其他 Java 线程可以中断该 Java 线程。 因此,当前的 Java 线程必须立即返回。
getHoldCount() 返回资源上的锁数量。
isHeldByCurrentThread() 当当前Java线程使用锁时,该方法返回true。

使用 ReentrantLock

在这个程序中,我们为ReentrantLock创建一个对象。 runnable类业务执行并将锁传递给ReentrantLock。

有两个池可以观察结果。 程序使用 lock() 获取锁,使用 unlock() 最终释放锁。

布尔完成和可用跟踪锁的可用性和任务完成情况。

有两个线程正在运行。 第二家商店没有获得锁并在队列中等待。

Shop 1 首先获得外锁,然后获得内锁。 此时2号店正在等待。

Shop 1 的锁定计数为 2。 店铺 1 释放内部锁,然后释放外部锁,然后锁保持计数减少,店铺 1 关闭。

Shop2通过队列自动获得锁。 对商店 2 重复该过程。

示例代码:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
class business implements Runnable
{
  String name;
  ReentrantLock re;
  public business(ReentrantLock rl, String n)
  {
    re=rl;
    name=n;
  }
  public void run()
  {
    boolean finished=false;
    while (!finished)
    {
      //Get Outer Lock
      boolean isLockAvailable = re.tryLock();
      //If the lock is available
      if(isLockAvailable)
      {
        try{
          Date d=new Date();
          SimpleDateFormat ft=new SimpleDateFormat("hh:mm:ss");
          System.out.println("Shop  - "+ name
                     + " got outside lock at "
                     + ft.format(d)
                     );

          Thread.sleep(1500);
          // Get Inner Lock
          re.lock();
          try{
            d=new Date();
            ft=new SimpleDateFormat("hh:mm:ss");
            System.out.println("Shop - "+ name
                       + " got inside lock at "
                       + ft.format(d)
                       );

            System.out.println("Lock Hold Count - "+ re.getHoldCount());
            Thread.sleep(1500);
          }
          catch(InterruptedException e){
            e.printStackTrace();
          }
          finally
          {
            //Inner lock release
            System.out.println("Shop - " + name +
                       " releasing inside lock");

            re.unlock();
          }
          System.out.println("Lock Hold Count - " + re.getHoldCount());
          System.out.println("Shop - " + name + " closed");
          finished=true;
        }
        catch(InterruptedException e){
          e.printStackTrace();
        }
        finally{
          //Outer lock release
          System.out.println("Shop - " + name +
                     " releasing outside lock");
          re.unlock();
          System.out.println("Lock Hold Count - " +
                       re.getHoldCount());
        }
      }
      else
      {
        System.out.println("Shop - " + name +
                      " waiting for lock");
        try{
          Thread.sleep(1000);
        }
        catch(InterruptedException e){
          e.printStackTrace();
        }
      }
    }
  }
}
public class sample
{
  static final int POOL_MAX=2;
  public static void main(String[] args)
  {
    ReentrantLock rel=new ReentrantLock();
    ExecutorService pool=Executors.newFixedThreadPool(POOL_MAX);
    Runnable p1=new business(rel, "Shop 1");
    Runnable p2=new business(rel, "Shop 2");
    System.out.println("Running Pool 1");
    pool.execute(p1);
    System.out.println("Running Pool 2");
    pool.execute(p2);
    pool.shutdown();
  }
}

输出:

Running Pool 1
Running Pool 2
Shop - Shop 2 waiting for the lock
Shop  - Shop 1 got the outside lock at 11:05:47
Shop - Shop 2 waiting for the lock
Shop - Shop 1 got the inside 'lock' at 11:05:48
Lock Hold Count - 2
Shop - Shop 2 waiting for the lock
Shop - Shop 2 waiting for the lock
Shop - Shop 1 releasing the inside lock
Lock Hold Count - 1
Shop - Shop 1 closed
Shop - Shop 1 releasing the outside lock
Lock Hold Count - 0
Shop  - Shop 2 got the outside lock at 11:05:51
Shop - Shop 2 got the inside 'lock' at 11:05:52
Lock Hold Count - 2
Shop - Shop 2 releasing the inside lock
Lock Hold Count - 1
Shop - Shop 2 closed
Shop - Shop 2 releasing the outside lock
Lock Hold Count - 0

使用二进制或计数信号量

Java中的信号量决定了一起访问资源的线程数量。 二进制信号量在多线程编程中提供访问资源的通行证或许可。

Java 线程需要等待,直到锁许可可用。 acquire()方法给出通行证,release()函数释放锁通行证。

acquire() 函数会阻塞线程,直到锁许可变得可用。

信号量中可用的两种状态是许可证可用和许可证不可用。

在此示例中,Semaphore 对象访问 commonResource() 函数。 程序中正在运行两个线程。

acquire() 方法向线程授予许可,release() 方法根据可用性释放锁许可。

线程0获得通行证并进入繁忙空间。 然后,在释放许可证后,它就会进入自由空间。

接下来,线程1获得锁许可,进入临界区,释放许可后来到空闲空间。

示例代码:

import java.util.concurrent.Semaphore;
public class SemaphoreCounter {
    Semaphore binary=new Semaphore(1);
    public static void main(String args[]) {
        final SemaphoreCounter semObj=new SemaphoreCounter();
        new Thread(){
            @Override
            public void run(){
              semObj.commonResource();
            }
        }.start();
        new Thread(){
            @Override
            public void run(){
              semObj.commonResource();
            }
        }.start();
    }
    private void commonResource() {
        try {
            binary.acquire();
            //mutual sharing resource
            System.out.println(Thread.currentThread().getName() + " busy space");
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        } finally {
            binary.release();
            System.out.println(Thread.currentThread().getName() + " free space");
        }
    }
}

输出:

Thread-0 busy space
Thread-0 free space
Thread-1 busy space
Thread-1 free space

使用原子变量

原子变量通过其内置函数在 Java 多线程编程中提供同步。

该程序设置计时器限制和线程大小。 executorService 通过循环到线程限制来提交计时器并关闭服务。

Atomic变量的定时器对象增加了定时器。 整个过程以同步方式发生。

每个 Java 线程都会获得唯一的计数而不会中断。

示例代码:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

class Timer implements Runnable {
    private static AtomicInteger timer;
    private static final int timerLimit=10;
    private static final int totThreads=5;
    public static void main(String[] args) {
        timer = new AtomicInteger(0);
        ExecutorService executorService = Executors.newFixedThreadPool(totThreads);
        for (int i = 0; i < totThreads; i++) {
            executorService.submit(new Timer());
        }
        executorService.shutdown();
    }
    @Override
    public void run() {
        while (timer.get() < timerLimit) {
            increaseTimer();
        }
    }
    private void increaseTimer() {
        System.out.println(Thread.currentThread().getName() + " : " + timer.getAndIncrement());
    }
}

输出:

pool-1-thread-2 : 4
pool-1-thread-2 : 5
pool-1-thread-4 : 1
pool-1-thread-4 : 7
pool-1-thread-4 : 8
pool-1-thread-4 : 9
pool-1-thread-3 : 3
pool-1-thread-5 : 2
pool-1-thread-1 : 0
pool-1-thread-2 : 6

本文教会了我们在多线程编程中Java中同步资源的四种方法。 同步方法是传统方法。

我们采用 ReentrantLock 方法来提高灵活性。 ReentrantLock也有一个缺点:程序员在编写 lock()unlock() 时需要跟踪try-catch-finally块。

尽管 Binary Semaphore 与 ReentrantLock 类似,但 ReentrantLock 仅具有基础级别的同步和固定锁定机制。 二进制信号量提供高级同步、自定义锁定和死锁预防。

原子变量也是一种实现简单数据读写活动同步的方法。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

如何在 Java 中延迟几秒钟的时间

发布时间:2023/12/17 浏览次数:217 分类:Java

本篇文章主要介绍如何在 Java 中制造程序延迟。本教程介绍了如何在 Java 中制造程序延时,并列举了一些示例代码来了解它。

如何在 Java 中把 Hashmap 转换为 JSON 对象

发布时间:2023/12/17 浏览次数:187 分类:Java

它描述了允许我们将哈希图转换为简单的 JSON 对象的方法。本文介绍了在 Java 中把 Hashmap 转换为 JSON 对象的方法。我们将看到关于创建一个 hashmap,然后将其转换为 JSON 对象的详细例子。

如何在 Java 中按值排序 Map

发布时间:2023/12/17 浏览次数:171 分类:Java

本文介绍了如何在 Java 中按值对 Map 进行排序。本教程介绍了如何在 Java 中按值对 Map 进行排序,并列出了一些示例代码来理解它。

如何在 Java 中打印 HashMap

发布时间:2023/12/17 浏览次数:192 分类:Java

本帖介绍了如何在 Java 中打印 HashMap。本教程介绍了如何在 Java 中打印 HashMap 元素,还列举了一些示例代码来理解这个主题。

在 Java 中更新 Hashmap 的值

发布时间:2023/12/17 浏览次数:146 分类:Java

本文介绍了如何在 Java 中更新 HashMap 中的一个值。本文介绍了如何在 Java 中使用 HashMap 类中包含的两个方法-put() 和 replace() 更新 HashMap 中的值。

Java 中的 hashmap 和 map 之间的区别

发布时间:2023/12/17 浏览次数:79 分类:Java

本文介绍了 Java 中的 hashmap 和 map 接口之间的区别。本教程介绍了 Java 中 Map 和 HashMap 之间的主要区别。在 Java 中,Map 是用于以键值对存储数据的接口,

在 Java 中获取用户主目录

发布时间:2023/12/17 浏览次数:218 分类:Java

这篇文章向你展示了如何在 Java 中获取用户主目录。本教程介绍了如何在 Java 中获取用户主目录,并列出了一些示例代码以指导你完成该主题。

Java 中 size 和 length 的区别

发布时间:2023/12/17 浏览次数:179 分类:Java

这篇文章教你如何知道 Java 中大小和长度之间的区别。本教程介绍了 Java 中大小和长度之间的区别。我们还列出了一些示例代码以帮助你理解该主题。

Java 中的互斥锁

发布时间:2023/12/17 浏览次数:111 分类:Java

了解有关 Java 中互斥锁的一切,在计算机科学领域,互斥或互斥被称为并发控制的属性。每台计算机都使用称为线程的最小程序指令序列。有一次,计算机在一个线程上工作。为了更好地理解,

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便