JUC并发相关

10. 并发终结之Volatile

2020-09-19  本文已影响0人  涣涣虚心0215

volatile关键字的作用:保障可见性、保障有序性以及保障long/double类型的变量读写操作的原子性
需要注意的是volatile仅仅只能保障被修饰变量的读、写操作的原子性,且这个赋值的过程不能涉及任何共享变量(比如volatile a++或者volatile a = volatile b + 1)。
注意一点volatile Object o = new Object(),虽然之前说new Object()操作可以分为三步:
1 objRef = allocate(Object.class)分配Object实例所需要的内存空间,并获得一个指向该空间的引用
2 invokeConstructor(objRef)调用Object的构造器初始化objRef所指向的Object实例
3 o = objRef 将实例引用objRef赋值给实例变量o
而volatile仅仅只能保证第3步赋值操作的原子性,但是步骤1,2不涉及共享变量,所以整个赋值操作都可以看做是原子操作,且volatile避免了2,3步骤的重排序,可以解决单例模式双重检测锁DCL的问题。

volatile的可见性

volatile变量的写操作类似释放锁的效果,volatile变量的读操作类似获取锁的效果。

public class TestVolatile {
    private volatile boolean result = false;
    public void writer() {
        result = true;
        System.out.println("set");
    }
    public static void main(String[] args) {
        TestVolatile test = new TestVolatile();
        new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            test.writer();
        }).start();
        while (!test.result) {
        }
        System.out.println("------");
    }
}
======
如果不加volatile就只打印set,不能正常退出
如果加了volatile
set
------
并正常退出

Volatile可以看做是给JIT的一个提示,相当于告诉JIT相应的变量值可能被其他线程修改,从而使JIT不去做一些优化,而避免可见性问题。
此外针对于volatile修饰的数组或者引用类型变量,volatile只能保证读线程能够读到共享变量的相对新值,而不能保证相应对象内部字段(实例变量、类变量)的相对新值。
Volatile与锁相比,是一种更轻量级的同步机制,因为在使用这些变量时不会发生上下文切换或线程调度等操作。但是比普通读写更高,因为处理器缓存失效,每次都要去主内存中读取。

上一篇 下一篇

猜你喜欢

热点阅读