一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
内存可见性:即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的
内存屏障(memory barrier):如果你的字段是volatile,Java内存模型将在写操作后插入一个写屏障指令,在读操作前插入一个读屏障指令。这意味着如果你对一个volatile字段进行写操作,你必须知道:1、一旦你完成写入,任何访问这个字段的线程将会得到最新的值。2、在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数据值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存
例如你让一个volatile的integer自增(i++),其实要分成3步:1)读取volatile变量值到local; 2)增加变量的值;3)把local的值写回,让其它的线程可见。这3步的jvm指令为
mov 0xc(%r10),%r8d ; Load inc %r8d ; Increment mov %r8d,0xc(%r10) ; Store lock addl $0x0,(%rsp) ; StoreLoad Barrier
注意最后一步是内存屏障,写屏障指令
内存可见性
/** * volatile可以保证属性的可见性,一个线程修改了,另一个线程可以立刻知道该线程被修改了 * */ public class VolatileTest { private static volatile int INIT_VALUE = 0; private final static int MAX_LIMIT = 5; public static void main(String[] args) { new Thread(() -> { int localValue = INIT_VALUE; while (INIT_VALUE < MAX_LIMIT) { System.out.printf("Update the value to [%d] ", ++localValue); INIT_VALUE = localValue; try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }, "UPDATER").start(); new Thread(() -> { while (INIT_VALUE < 3) { } System.out.println("结束循环了"); }, "READER").start(); } }
内存屏障,防止重排序
class Singleton { private volatile static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if(instance==null) { synchronized (Singleton.class) { if(instance==null) instance = new Singleton(); } } return instance; } }
volatile不具备原子性
public class VolatileTest { private static volatile long value = 0; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new LoopVolatile()); t1.start(); Thread t2 = new Thread(new LoopVolatile2()); t2.start(); t1.join(); t2.join(); System.out.println("final val is: " + value); // final val is: 18585 } private static class LoopVolatile implements Runnable { public void run() { long val = 0; while (val < 10000L) { value++; val++; } } } private static class LoopVolatile2 implements Runnable { public void run() { long val = 0; while (val < 10000L) { value++; val++; } } } }
CPU高速缓存
JMM java内存模型