多线程:happens-before原则

下面就来具体介绍下happens-before原则(先行发生原则):

·        程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作

·        锁定规则一个unlock操作先行发生于后面对同一个锁的lock操作。(先解锁,才可以在后面继续上锁)

·        volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作

·        传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C

·        线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作

·        线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生

·        线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行(所有终结的线程都不可再用)

·        对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

这8条原则摘自《深入理解Java虚拟机》。

  这8条规则中,前4条规则是比较重要的,后4条规则都是显而易见的。


 

  下面我们来解释一下前4条规则:

  对于程序次序规则来说,我的理解就是一段程序代码的执行在单个线程中看起来是有序的。注意,虽然这条规则中提到“书写在前面的操作先行发生于书写在后面的操作”,这个应该是程序看起来执行的顺序是按照代码顺序执行的,因为虚拟机可能会对程序代码进行指令重排序。虽然进行重排序,但是最终执行的结果是与程序顺序执行的结果一致的,它只会对不存在数据依赖性的指令进行重排序。因此,在单个线程中,程序执行看起来是有序执行的,这一点要注意理解。事实上,这个规则是用来保证程序在单线程中执行结果的正确性,但无法保证程序在多线程中执行的正确性

  第二条规则也比较容易理解,也就是说无论在单线程中还是多线程中,同一个锁如果出于被锁定的状态,那么必须先对锁进行了释放操作,后面才能继续进行lock操作

  第三条规则是一条比较重要的规则,也是后文将要重点讲述的内容。直观地解释就是,如果一个线程先去写一个变量,然后一个线程去进行读取,那么写入操作肯定会先行发生于读操作。

  第四条规则实际上就是体现happens-before原则具备传递性


一个操作,时间上先发生不代表这个操作就一定是先行发生。反过头来也一样。一个操作先行发生也不一定这个操作就是死时间上的先发生!!!

时间先后顺序和先行发生原则之间没有基本没有太大的关系!所以我们在衡量并发安全问题的时候不要受到时间顺序的干扰,一切顺序必须先以先行发生原则进行! 

 


再来一遍

 

先行发生原则   ====》》》》     happens-before原则

上面提到了可以用 volatile 和 synchronized 来保证有序性。除此之外,JVM 还规定了先行发生原则,让一个操作无需控制就能先于另一个操作完成。

1. 单一线程原则

Single Thread rule

在一个线程内,在程序前面的操作先行发生于后面的操作。

 

2. 管程锁定规则

Monitor Lock Rule

一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。

 

3. volatile 变量规则

Volatile Variable Rule

对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。

 

4. 线程启动规则

Thread Start Rule

Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。

 

5. 线程加入规则

Thread Join Rule

Thread 对象的结束先行发生于 join() 方法返回。

 

6. 线程中断规则

Thread Interruption Rule

对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。

7. 对象终结规则

Finalizer Rule

一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。

8. 传递性

Transitivity

如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。

你可能感兴趣的:(多线程:happens-before原则)