解释 volatile
原理:使用内存指令,保证线程在获取变量时的可见性,每个线程在执行前会拷贝内存中变量到工作线程中使用,变量修改后再写回主内存,并通知其他变量拥有者线程变量更新(可见性)。
保证内存可见性,不保证原子性,禁止指令排序
指令重排:编译器为了优化代码,从而会将一些数据不想关的代码重新排序,这个导致在多线程中由于顺序被打乱了而导致数据与预期不同。所以多线程防止指令重排序要用volatile。
不保证原子性:因为多线程下更新一个值是,可能A刚写完就被B写入了,B还没收到A更新后的值。
原理解释
内存可见性的实现方式是: 当一个线程修改了volatile修饰的变量时,马上修改线程内的副本值并让内存种的值失效。当其他线程来访问时,发现内存种的值失效了,会重新load新的值到线程副本中。
禁止重排序的实现方式:
happens-before: A happens-before B, B happens-before C, 则 A happens-before C;
在编译成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
什么是内存屏障?
硬件层面,内存屏障分两种:读屏障(Load Barrier)和写屏障(Store Barrier)。
内存屏障有两个作用:
阻止屏障两侧的指令重排序;
强制把写缓冲区/高速缓存中的脏数据等写回主内存,或者让缓存中相应的数据失效
编译器选择了一个比较保守的JMM内存屏障插入策略,这样可以保证在任何处理器平台,任何程序中都能得到正确的volatile内存语义。这个策略是:
在每个volatile写操作前插入一个StoreStore屏障;
在每个volatile写操作后插入一个StoreLoad屏障;
在每个volatile读操作后插入一个LoadLoad屏障;
在每个volatile读操作后再插入一个LoadStore屏障。
再介绍一下volatile与普通变量的重排序规则:
如果第一个操作是volatile读,那无论第二个操作是什么,都不能重排序;
如果第二个操作是volatile写,那无论第一个操作是什么,都不能重排序;
如果第一个操作是volatile写,第二个操作是volatile读,那不能重排序。
Last updated