首页 > 技术文章 > volatile

Nilekai 2021-12-20 17:02 原文

volatile的作用

volatile是Java虚拟机提供的轻量级的同步机制。

  1. 可见行:被volatile修饰的属性在工作内存被修改后,会将值刷新至主内存,因此任何时刻,不同线程总能看到该变量的最新值,保证了可见性。
  2. volatile修饰的属性不能保证原子性操作,即不保证原子性
  3. 防止指令重排

验证可见行

public class Demo {
     //如果将volatile去除那么程序将一直运行
    volatile int i = 0;
    public static void main(String[] args) {
        checkVolatile();
    }
    public static void checkVolatile() {
        Demo demo = new Demo();
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            demo.i = 60;}).start();
	 System.out.println("update i = 60");
        //主线程
        while (demo.i == 0) {

        }
    }
}

验证不保证原子性

public class Demo {
    volatile int i = 0;
    //synchronized
    public void addI() {
        i++;
    }
    //开启20个线程,每个线程相加1000次,理论上应为20000,但是实际执行却不是小于理论值
    public static void main(String[] args) {
       Demo demo = new Demo();
       for (int i =0; i < 20; i++){
           new Thread(() -> {
               for (int j = 0; j < 1000; j++) {
                   demo.addI();
               }
           }).start();
       }
       //判断是否还有其他线程在运行
       //后台默认两个线程主线程和GC线程
       while (Thread.activeCount() > 2) {
           Thread.yield();
       }
        System.out.println(demo.i);
    }
}

验证防止指令重排

参考:https://www.infoq.cn/article/java-memory-model-2/

应用场景

单例模式(多线程下的单例)

public class SingletonDemo {
    //防止指令重排,对象初始化和分配对象内存存在指令重排的情况,导致会出现获取的instance等于null,导致出错
    private static volatile SingletonDemo instance;

    private SingletonDemo() {
        System.out.println("SingletonDemo instance!");
    }
    //加锁前后进行判断是否已经实例化(双端检索)
    public static SingletonDemo getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }
}

推荐阅读