Java并发编程(二):Java 内存模型

105 阅读3分钟

什么是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的操作。

其实还有两条规则:

  1. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。
  2. 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。 这两条规则使用不怎么常见,所以主要谨记上面六条规则就OK了。