来源:互联网 发布:阿里云机房地址 编辑:程序博客网 时间:2024/05/03 05:03

上一篇的解释是繁琐的不行,连虚拟机这本书的作者也是如此的说。但是在这一节会用些简单的描述来定义这些规则。

这就是java语言内的一个原则“先行发生”的原则(happens-before)原则,这个原则是判断数据是否存在竞争,线程是否安全的主要依据。靠这个原则可以通过几条规则解决并发环境下两个操作之间是否可能存在竞争。

先行发生原则是java内存模型中定义的两项操作之间的偏序关系,也就是如果A HB  B,也就表明B发生之前,A产生的影响能被B观察到,影响包括内存中共享变量的值,发送了消息,调用了方法等。

下面来看java内存模型下一些“天然的”HB关系,这些关系无需任何同步器协助就已经存在。如果两个操作之间的关系不在此列,并且无法从下列规则导出的话,它们就没有顺序性保障,虚拟机可以对它们进行随意的排序。

1. 程序次序规则:在一个线程内,按照程序代码顺序,在前面的操作 HB 于 书写在后面的操作。(准确的说应该是控制流顺序并不是程序代码顺序,因为存在分支,循环结构)。

2. 管程锁定原则:一个unlock的操作 HB 于后面对同一个锁的lock操作(必须是同一把锁,后面指的是时间上的先后)。

3.volatile变量规则:一个volatile变量的写操作 HB 于 后面对这个变量的读操作(后面指的是时间上的先后)。

4.线程启动规则:Thread对象的start()方法 HB 于此线程的每一个动作。

5.线程终止规则:线程中的所有操作都 HB于对此线程的终止检测。

6.线程中断规则:对线程interrupt()操作的调用 HB 于被中断线程的代码检测到中断事件的发生。也就是 Thread.interrupted()检测方法。

7.对象终结规则:一个对象的初始化完成 HB 于 它的 finalize()方法的开始。

8.传递性:如果 A HB B,B HB C,那么A HB C。


private int num = 0;pubic void setNum(int num){this.num = num;}public void getNum(){return num;}
上面是一个很常见的get/set操作。假设线程A和线程B,A先setNum(1),然后线程B调用同一个对象的getNum()。

稍微接触过多线程的,就能知道,这边线程B并不能得到正确的值 1。为什么呢?可以通过上面的规则来分析其原理。


1.两个方法分别由A,B调用,不在一个线程内,所以 规则 1(程序次序顺序)不适用

2.由于没有同步块,自然没有lock和unlock操作,所以规则 2(管程锁定规则)不适用

3.volatile规则也不适用,其他线程方面的规则更谈不上。

4.传递性也就无从谈起。


因此这里面的操作并不是线程安全的。线程B得到什么值也就不能确定了。

















原创粉丝点击