什么是Java内存模型
我们知道,可见性和有序性问题是由CPU缓存和编译优化所造成的,那么我们要解决这两个问题就只需禁用CPU缓存和编译优化就行了,但为了保证程序的性能,充分利用缓存和编译器,我们应该按需禁用CPU缓存和编译优化。而Java内存模型就是一套规则体系来规范了我们如何按需禁用CPU缓存和编译优化,其中包括了关键字synchronized、volatile和final,还有六个Happens Before原则。 这篇我们着重讲六个Happens Before原则。
六大Happens Before原则
什么是Happens Before?
直接直译过来是发生在xxx之前的意思,实际上在程序中,前一个操作的结果对后面操作是可见的,这就是Happens Before。 由此可见,这些原则主要解决了可见性和有序性问题,原则的内容主要有下面六条:
程序的顺序性原则
这一条十分容易理解,指在单一线程中,前一个操作的结果Happens Before后续任意操作。
volatile的读写规则
这一条是针对volatile变量的,讲的是对volatile变量的写Happens Before后续对于该volatile变量的读。
传递性
这条十分十分重要!!内容是,如果A Happens Before B, B Happens before C, 那么 A Happens Before C。
这里有一段代码可以加深下1、2、3规则的理解:
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void write() {
x = 42;
v = true;
}
public void read() {
if( v == true) {
....
}
这里如果线程A先执行了write()函数,那么若线程B在这之后执行read()函数,进入if条件后,是可以看见x的值是42的,这是因为对x值的写是在对volatile变量写的之前,根据1、2、3条规则,在之后执行read函数时就能看见x的值时42了。
管程中的锁的规则
这条指的是对一个锁的解锁 Happens Before 对一个锁的释放。
Java中synchronized是隐式锁,意味着在进入该锁保护的临界区时,会自动获取锁,离开时会自动释放锁。
线程的start()规则
这条指的是当主线程A启动子线程B后,子线程B能够看到主线程A在启动子线程B之前的所有操作。
线程的join()规则
这条指的是当主线程A在等待子线程B完成后,主线程A能够看到所有子线程B的操作。
其实还有两条规则:
- 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。
- 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。 这两条规则使用不怎么常见,所以主要谨记上面六条规则就OK了。