volatile关键字原理

可见性

可见性产生的原因

硬件层面

CPU的执行速度远远大于从主存读取的速度,所以为了尽可能弥补主存的读取这一瓶颈,在CPU和主存之际还有一层高速cache。现在大多都是多核cpu,每个核独有一个cache。
所以当运行在一个核上的一个线程对一个变量进行修改后,其实最先修改的是当前cache里的值,然后cache会将此值同步到主内存,然后再由其他核上的cache同步。由于这种关系,一个线程修改了一个变量,对于其他线程并不是立马可以感知到的。实际上,如果没有触发同步指令,也可能永远感知不到变化。

JMM

Java作为高级语言,屏蔽了CPU cache等底层细节,用 JMM 定义了一套读写内存数据的规范,虽然我们不再需要关心一级缓存和二级缓存的问题,但是,JMM 抽象了主内存和本地内存的概念。

解决方法

采用这种cache机制,以及jvm在此基础上抽象出的多JMM模型,本质上都是为了提高多线程下CPU执行的效率,但是也是因为这种机制,造成了线程之间的可见性问题。
想要保证线程之间的可见,要么触发同步指令,要么加上volatile关键字,被volatile修饰的内存,只要有修改,立马会同步涉及到的线程。

有序性

CPU中的乱序执行

cpu在底层对代码进行优化,为了提升效率,可能会对代码执行的顺序进行重排序。
代码的执行顺序,这里的代码是具体是指java翻译为汇编后的执行顺序

线程的as-if-serial

在单线程中,从上往下的语句,未必是按照顺序执行。
cpu对单线程的重排序执行,不会影响执行的结果,保证了最终的一致性。
as-if-serial,可以翻译为看上去像是顺序执行,其实也就是代表在单线程中重排序不会对代码结果有影响。但是在多线程中可能会导致线程不安全的问题。

volatile实现可见性

内存屏障

内存屏障是汇编语言中特殊的指令:cpu看到这条指令,前面的必须执行完成,后面的才能执行。
在intel的cpu中,内存屏障的指令是:lfence、sfence、mfence

jvm中的内存屏障

jvm对汇编中的内存屏障有自己的抽象。
所有实现jvm规范的虚拟机,必须实现四个屏障
LoadLoadBarrier,LoadStoreBarrier,StoreLoadBarrier,StoreStoreBarrier

volatile的底层实现

volatile修饰的内存,不可以重排序。
其实质是对volatile修饰变量的读写访问,都不可换顺序。

DLC单例要不要加上volatile

public class SingletonDLC {

    private SingletonDLC() {
    }

    private SingletonDLC instance;

    public SingletonDLC getInstance() {
        //第一个if是提升性能,如果instance!=null则不需要去获取锁
        if (instance == null) {
            synchronized (SingletonDLC.class) {
                //第二个if是必要的
                if (instance == null) {
                    instance = new SingletonDLC();
                }
            }
        }
        return instance;
    }

}
对象new的过程
class T{
    int m = 8;
}
T t = new T();

对于一个类的new的过程,对应的汇编码为

0 new #2 <T> //在堆内存中分配对象
3 dup
4 invokespecial #3 <T.<init>> //执行构造方法
7 astore_1 //将t执行这块内存
8 return

由汇编码可见,主要分为三步:
1、在堆中分配内存,并且赋予初值,这里的初值是jvm提供的,不是构造方法。m变量在这一步将会赋值为0;
2、执行T的构造方法;
3、将t执行这块内存
如果在单线程中2,3两步的执行顺序不会影响结果,所以cpu可能会对其进行乱序,也就是先将t执行这块区域,然后在执行构造方法。
在单线程中虽然没有影响,但是在多线程中却存在很大问题。

比如线程A在进行new的时候,如果先执行3,则t==null这个条件对于其他线程就不满足类,所以其他线程在执行getInstance的时候会返回这个句柄,但是其实里面的成员值并没有执行构造方法,所以其他线程拿到的其实是一个半初始化状态的对象。

posted @ 2020-11-22 18:15  刃牙  阅读(183)  评论(0编辑  收藏  举报