JUC并发编程 原理之 volatile -- 保证可见性 & 保证有序性 & 习题 balking模式

Posted Z && Y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC并发编程 原理之 volatile -- 保证可见性 & 保证有序性 & 习题 balking模式相关的知识,希望对你有一定的参考价值。

1. 保证可见性

  • 写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中
public void actor2(I_Result r) {
 num = 2;
 ready = true; // ready 是 volatile 赋值带写屏障
 // 写屏障
}
  • 读屏障(lfence)保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据
public void actor1(I_Result r) {
 // 读屏障
 // ready 是 volatile 读取值带读屏障
 if(ready) {
 r.r1 = num + num;
 } else {
 r.r1 = 1;
 }
}

2. 如何保证有序性

  • 写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后
public void actor2(I_Result r) {
 num = 2;
 ready = true; // ready 是 volatile 赋值带写屏障
 // 写屏障
}
  • 读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前
public void actor1(I_Result r) {
 // 读屏障
 // ready 是 volatile 读取值带读屏障
 if(ready) {
 r.r1 = num + num;
 } else {
 r.r1 = 1;
 }
}

3. 小结

volatile 不能解决指令交错:

  • 写屏障仅仅是保证之后的读能够读到最新的结果,但不能保证读跑到它前面去
  • 而有序性的保证也只是保证了本线程内相关代码不被重排序

4. balking模式

希望 doInit() 方法仅被调用一次,下面的实现是否有问题,为什么?

分析:

  • initialized 被 volatile 修饰 只可以保证变量的可见性,并不能保证多行代码执行的原子性。
  • volatile 适用于一个线程写 另外一些线程在读的情况。还可以用于代码在必要的时间禁用指令重排序。

改正方法:

    public class TestVolatile {
        Boolean initialized = false;

        void init() {
            synchronized (initialized) {
                if (initialized) {
                    return;
                }
                doInit();
                initialized = true;
            }
        }

        private void doInit() {
        }
    }


以上是关于JUC并发编程 原理之 volatile -- 保证可见性 & 保证有序性 & 习题 balking模式的主要内容,如果未能解决你的问题,请参考以下文章

JUC并发编程 详解Java关键字之 volatile

JUC并发编程 详解Java关键字之 volatile

JUC并发编程 共享模式之工具 JUC ReentrantLock -- ReentrantLock原理

JUC并发编程(10)--- 谈谈对Volatile的理解

JUC并发编程 共享模型之内存 -- Java 内存模型 & 原子性 & 可见性(可见性问题及解决 & 可见性 VS 原子性 & volatile(易变关键字))(代码

Java 并发JUC数据结构CopyOnWriteArrayList原理