从JDK5开始,Java使用新的JSR-133内存模型;
JSR-133使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间
Happen-Before规则
- 程序次序规则(Program Order Rule)
在一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。准确地说应该是控制流顺序而不是代码顺序,因为要考虑分支、循环等结构。
- 监视器锁定规则(Monitor Lock Rule)
一个unlock操作先行发生于后面对同一个对象锁的lock操作。这里强调的是同一个锁,而“后面”指的是时间上的先后顺序,如发生在其他线程中的lock操作。
- volatile变量规则(Volatile Variable Rule)
volatile变量的写先发生于读,这保证了volatile变量的可见性
- 线程启动规则(Thread Start Rule)
Thread独享的start()方法先行于此线程的每一个动作。
- 线程终止规则(Thread Termination Rule)
线程中的每个操作都先行发生于对此线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值检测到线程已经终止执行。
- 线程中断规则(Thread Interruption Rule)
对线程interrupte()方法的调用优先于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测线程是否已中断。
- 对象终结原则(Finalizer Rule)
一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
- 传递性(Transitivity)
如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。
说明:正是以上这些规则保障了happen-before的顺序,如果不符合以上规则,那么在多线程环境下就不能保证执行顺序等同于代码顺序,也就是“如果在本线程中观察,所有的操作都是有序的;如果在一个线程中观察另外一个线程,则不符合以上规则的都是无序的”,因此,如果我们的多线程程序依赖于代码书写顺序,那么就要考虑是否符合以上规则,如果不符合就要通过一些机制使其符合,最常用的就是synchronized、Lock以及volatile修饰符。
参考资料
【happens-before俗解】http://ifeve.com/easy-happens-before/
【死磕Java并发--Java内存模型之happens-before】 http://cmsblogs.com/?p=2102