JSR内存屏障:
- LoadLoad:对于这样的语句
Load1;LoadLoad;Load2
,在Load2及后续的读操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕; - StoreStore:对于这样的语句
Store1;StoreStore;Store2
,在Store2及后续的写操作执行前,保证Store1的写入操作对其他处理器可见; - LoadStore:对于这样的语句
Load1;LoadStore;Store2
,在Store2及后续的写入操作被刷出前,保证Load1要读取的数据被读取完毕; - StoreLoad:对于这样的语句
Store1;StoreLoad;Load2
,在Load2及后续的读操作要读取的数据被访问前,保证Store1的写入操作对其他处理器可见;
as-if-serial:
不管如何重排序,单线程执行结果不会改变。在JVM层面volatile的实现细节特别保守,保证了内存可见性,并且成功防止指令重排序。如果是对对象加入volatile,是对该对象前后加入内存屏障,具体的实现细节如下所示:
StoreStoreBarrier
volatile 写操作
StoreLoadBarrier
LoadLoadBarrier
volatile 读操作
LoadStoreBarrier
happens-before原则:
happens-before原则是Java内存模型中定义的两个操作之间的偏序关系。比如说操作A先行发生于操作B,那么在B操作发生之前,A操作产生的“影响”都会被操作B感知到。这里的影响是指修改了内存中的共享变量、发送了消息、调用了方法等。
- 程序顺序规则: 一个线程中的每个操作,happens-before于该线程中的任意后续操作;
- 监视器锁规则: 对每一个锁的解锁,happens-before于随后对该锁的加锁;
- Volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读;
- 线程启动规则:Thread的start()方法先行发生于这个线程的每一个操作;
- 线程终止原则:线程的所有操作都先行于此线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测线程的终止;
- 线程中断原则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupt方法检测线程是否中断;
- 对象终结规则:一个对象的初始化完成先于发生它的finalize()方法的开始;
- 传递性: 如果A happens-before B, B happens-before C, 那么A happens-before C;
volatile关键字修饰变量的特殊规则:
- 可见性:对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
- 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。
final域的重排序规则:
- 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序;
- 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序;