Java volatile关键字的作用

1.可见性

JVM定义了线程与主内存之间的抽象关系:共享变量存储在主内存,每个线程都有一个私有的本地内存,本地内存保存了该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在本地内存中进行,而不能直接读写主内存的变量。

Java volatile关键字的作用_第1张图片

 

 

例如下面的程序

Java volatile关键字的作用_第2张图片

运行上述的代码,你会发现 在main方法打印 running is false 之后,程序并没有正常退出,而是一直在跑着 while(running) 这个死循环。但是当我们尝试把变量 running 加上 volatile 后在运行,程序就能正常退出了。 这是因为线程不会把被 volatile 修饰的变量拷贝到线程的本地内存,对变量的读写操作都绕过线程本地内存直接去访问主内存,这样就保证了变量在线程之间的可见性,因为我们访问的同一个变量,而不是变量的副本。

 

另外如下图,在m方法的死循环里随便打印个什么,程序也能正常结束输出 m end!但这个不是我们要讲的重点,还是关注volatile吧,O(∩_∩)O哈哈~

Java volatile关键字的作用_第3张图片

另外注意:volatile只能保证变量的可见性,并不能保证操作的原子性,如果要保证原子性,可以使用synchronized

2. 禁止指令重排

了解指令重排前先了解一个概念 CPU乱序 ,上代码
Java volatile关键字的作用_第4张图片

可以看到,在循环了3694916次后,x = 0,y = 0 ,那如果CPU没有乱序执行那么 if (x == 0 && y == 0) 这段代码永远不会为 true,

System.out.println(String.format("第%s次 ( %s , %s ) ", i, x, y));

这段代码也永远不会执行,那么CPU为什么要乱序执行呢?提高效率,就和你上厕所要拿着手机一样,就和你烧开水的时候可以去听歌一样,你不是等着开水烧开了再去听歌,提高生活效率。

那CPU层面如何禁止重排序呢?

答:内存屏障,对某部分内存操作时前后添加屏障,屏障前后的操作不可以乱序执行。

JVM是通过对volatile关键字修饰的对象前后添加屏障实现禁止指令重排序的,在volatile写操作前后加StoreStoreBarrier,在volatile读操作前后加LoadLoadBarrier。
另外JVM规定重排序必须遵守8条hanppens-before原则。

3. DCL单例需不需要加volatile?

public class Demo2 {
    private static Demo2 INSTANCE;

    private Demo2() {}

    public static Demo2 getInstance() {
        if (INSTANCE == null) {
            synchronized (Demo2.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Demo2();
                }
            }
        }

        return INSTANCE;
    }
}

这段代码 private static Demo2 INSTANCE 需不需要加volatile?

未完待续....

 

 

 

 

你可能感兴趣的:(JDK源码,java)