解释 volatile


  • 原理:使用内存指令,保证线程在获取变量时的可见性,每个线程在执行前会拷贝内存中变量到工作线程中使用,变量修改后再写回主内存,并通知其他变量拥有者线程变量更新(可见性)。

  • 保证内存可见性,不保证原子性,禁止指令排序

    • 指令重排:编译器为了优化代码,从而会将一些数据不想关的代码重新排序,这个导致在多线程中由于顺序被打乱了而导致数据与预期不同。所以多线程防止指令重排序要用volatile。

    • 不保证原子性:因为多线程下更新一个值是,可能A刚写完就被B写入了,B还没收到A更新后的值。

原理解释

  • 内存可见性的实现方式是: 当一个线程修改了volatile修饰的变量时,马上修改线程内的副本值并让内存种的值失效。当其他线程来访问时,发现内存种的值失效了,会重新load新的值到线程副本中。

  • 禁止重排序的实现方式:

    1. happens-before: A happens-before B, B happens-before C, 则 A happens-before C;

    2. 在编译成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

什么是内存屏障?

  • 硬件层面,内存屏障分两种:读屏障(Load Barrier)和写屏障(Store Barrier)。

  • 内存屏障有两个作用:

    1. 阻止屏障两侧的指令重排序;

    2. 强制把写缓冲区/高速缓存中的脏数据等写回主内存,或者让缓存中相应的数据失效

      • 编译器选择了一个比较保守的JMM内存屏障插入策略,这样可以保证在任何处理器平台,任何程序中都能得到正确的volatile内存语义。这个策略是:

        • 在每个volatile写操作前插入一个StoreStore屏障;

        • 在每个volatile写操作后插入一个StoreLoad屏障;

        • 在每个volatile读操作后插入一个LoadLoad屏障;

        • 在每个volatile读操作后再插入一个LoadStore屏障。

      • 再介绍一下volatile与普通变量的重排序规则:

        • 如果第一个操作是volatile读,那无论第二个操作是什么,都不能重排序;

        • 如果第二个操作是volatile写,那无论第一个操作是什么,都不能重排序;

        • 如果第一个操作是volatile写,第二个操作是volatile读,那不能重排序。

Last updated