JAVA多线程之先行发生原则
一、引子
如果java内存模型中所有的有序性都仅仅依靠volatile和synchronized来完成,那么有一些操作会变得很繁琐,但我们在编写java并发代码时并未感觉到这一点,这是因为java语言中有个先行发生原则(happens-before),通过这个原则,我们可以通过几条规则一揽子解决并发环境下两个操作之间是否可能存在冲突的所有问题。
二、定义
先行发生是java内存模型中定义的两项做错之间的偏序关系,如果说操作A先行发生与操作B,其实就是说在发生操作B之前,操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。
三、规则
java内存模型中“天然的”的先行发生关系规则如下:
1、程序次序规则:
在一个线程内,按照程序代码顺序,,书写在前面的操作先行发生于书写在后面的操作,准确的说,应该是控制流程序而不是程序代码顺序,因为要考虑分支、循环等结构;
2、管程锁规则:
一个unlock操作先行发生于后面对同一个锁的lock操作,这里必须强调的是同一个锁,“后面”指的是时间上的先后顺序。
3、volatile变量规则:
对一个volatile变量的写操作先行发生于后面对这个变量的读操作,“后面”同样是时间上的先后顺序。
4、线程启动规则:
Thread对象的start()方法先行发生于次线程的每一个动作;
5、线程终止规则:
线程中的所有操作都先行发生于对此线程的终止检测,可以通过Thread.join()方法结束,Thread.isAlive()的返回值等手段检测到线程已终止执行
6、线程中断规则:
对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interruptd()方法检测到是否有中断发生
7、对象终结规则:
一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始
8、传递性
如果操作A先行发生于操作B,操作B先行发生于操作C,那么就可以认为操作A先行发生于操作C。
四、示例
如下案例:
public class TestValue { private int value = 0; public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
如果线程A调用了“setValue(1)”,线程B调用了同一个对象的“getValue()”方法,那么返回值是什么?
答案是不确定,因为这里的操作不是线程安全的,它不满足上述所有的 先行发生 规则。
修复方法有好多种,这里列举较为简单的两种:
1)把getter/setter方法都定义为synchronized方法
2)把value定义为volatile变量。