并发举措之二之volatile

     前言

    Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。volatile 变量具备两种特性,volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。volatile关键字的作用(变量可见性、禁止重排序)

     变量可见性:
    其一是保证该变量对所有线程可见,这里的可见性指的是当一个线程修改了变量的值,那么新的值对于其他线程是可以立即获取的。
    禁止重排序:
    volatile 禁止了指令重排。

  一、实现机制

  •     在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。
  •     volatile适合这种场景:一个变量被多个线程共享,线程直接给这个变量赋值。
  •     当对非volatile 变量进行读写的时候,每个线程先从内存拷贝变量到CPU缓存中。如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的CPU cache 中;
  •    当声明变量是volatile 的,JVM 保证了每次读变量都从内存中读,跳过CPU cache 这一步。
  •    因为jvm的操作不都是原子操作,如i++,++i等,所以volatile修饰的变量对于非原子操作并不能保证其线程安装。

 二、适用范围


(1)对变量的写操作不依赖于当前值(比如i++),或者说是单纯的变量赋值(boolean flag = true)。
(2)该变量没有包含在具有其他变量的不变式中,也就是说,不同的volatile变量之间,不能互相依赖。只有在状态真正独立于程序内其他内容时才能使用volatile。

三、实例应用

3.1、volatile替换sychronized

public class StopThread {
    private static Boolean stopRequested;
    private static synchronized void requestStop(){
        stopRequested=true;
    }
    private static synchronized Boolean stopRequested(){
        return stopRequested;
    }
    
    public static void main(String[]args)throws InterruptedException{
        Thread backgroundThread=new Thread(()->{
            int i=0;
            while(!stopRequested()){
                i++;
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        requestStop();
    }
}

 使用volatile关键字优化

public class StopThread {
    private static volatile Boolean stopRequested;
    public static void main(String[]args) throws InterruptedException{
        Thread backgroundThread = new Thread(()->{
            int i=0;
            while(!stopRequested){
                i++;
            }
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested=true;
    }
}

3.2、volatile的非原子操作

 错误的应用

    private static volatile int nextSerialNumber=0;

    public static int generateSerialNumber(){
        return nextSerialNumber++;
    }

 正确的应用

    private static final AtomicLong nextSerialNum = new AtomicLong();

    public static long generateSerialNumber(){
        return nextSerialNum.getAndIncrement();
    }

猜你喜欢

转载自blog.csdn.net/heijunwei/article/details/113126209
今日推荐