java内存模型的三大特性有哪些(解读Java内存模型中Happens-Before的8个原则)
摘要:本文我们就结合案例程序来说明Java内存模型中的Happens-Before原则 。
本文分享自华为云社区《【高并发】一文秒懂Happens-Before原则》 ,作者: 冰 河 。
在正式介绍Happens-Before原则之前 ,我们先来看一段代码 。
【示例一】
以上示例来源于:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong
这里,假设线程A执行writer()方法 ,按照volatile会将v=true写入内存;线程B执行reader()方法 ,按照volatile ,线程B会从内存中读取变量v ,如果线程B读取到的变量v为true ,那么 ,此时的变量x的值是多少呢??
这个示例程序给人的直觉就是x的值为42 ,其实 ,x的值具体是多少和JDK的版本有关 ,如果使用的JDK版本低于1.5,则x的值可能为42 ,也可能为0 。如果使用1.5及1.5以上版本的JDK ,则x的值就是42 。
看到这个,就会有人提出问题了?这是为什么呢?其实 ,答案就是在JDK1.5版本中的Java内存模型中引入了Happens-Before原则 。
接下来 ,我们就结合案例程序来说明Java内存模型中的Happens-Before原则 。
【原则一】程序次序规则
在一个线程中,按照代码的顺序 ,前面的操作Happens-Before于后面的任意操作 。
例如【示例一】中的程序x=42会在v=true之前执行 。这个规则比较符合单线程的思维:在同一个线程中 ,程序在前面对某个变量的修改一定是对后续操作可见的。
【原则二】volatile变量规则
对一个volatile变量的写操作 ,Happens-Before于后续对这个变量的读操作 。
也就是说 ,对一个使用了volatile变量的写操作 ,先行发生于后面对这个变量的读操作 。这个需要大家重点理解。
【原则三】传递规则
如果A Happens-Before B ,并且B Happens-Before C ,则A Happens-Before C 。
我们结合【原则一】 、【原则二】和【原则三】再来看【示例一】程序 ,此时 ,我们可以得出如下结论:
(1)x = 42 Happens-Before 写变量v = true,符合【原则一】程序次序规则 。
(2)写变量v = true Happens-Before 读变量v = true ,符合【原则二】volatile变量规则。
再根据【原则三】传递规则 ,我们可以得出结论:x = 42 Happens-Before 读变量v=true 。
也就是说,如果线程B读取到了v=true ,那么 ,线程A设置的x = 42对线程B就是可见的 。换句话说,就是此时的线程B能够访问到x=42 。
其实 ,Java 1.5版本的 java.util.concurrent并发工具就是靠volatile语义来实现可见性的 。
【原则四】锁定规则
对一个锁的解锁操作 Happens-Before于后续对这个锁的加锁操作 。
例如 ,下面的代码 ,在进入synchronized代码块之前 ,会自动加锁 ,在代码块执行完毕后 ,会自动释放锁 。
【示例二】
我们可以这样理解这段程序:假设变量x的值为10 ,线程A执行完synchronized代码块之后将x变量的值修改为10 ,并释放synchronized锁 。当线程B进入synchronized代码块时 ,能够获取到线程A对x变量的写操作,也就是说 ,线程B访问到的x变量的值为10 。
【原则五】线程启动规则
如果线程A调用线程B的start()方法来启动线程B ,则start()操作Happens-Before于线程B中的任意操作 。
我们也可以这样理解线程启动规则:线程A启动线程B之后,线程B能够看到线程A在启动线程B之前的操作。
我们来看下面的代码 。
【示例三】
上述代码是在线程A中执行的一个代码片段 ,根据【原则五】线程的启动规则 ,线程A启动线程B之后,线程B能够看到线程A在启动线程B之前的操作 ,在线程B中访问到的x变量的值为100 。
【原则六】线程终结规则
线程A等待线程B完成(在线程A中调用线程B的join()方法实现) ,当线程B完成后(线程A调用线程B的join()方法返回) ,则线程A能够访问到线程B对共享变量的操作。
例如 ,在线程A中进行的如下操作 。
【示例四】
【原则七】线程中断规则
对线程interrupt()方法的调用Happens-Before于被中断线程的代码检测到中断事件的发生 。
例如 ,下面的程序代码。在线程A中中断线程B之前 ,将共享变量x的值修改为100 ,则当线程B检测到中断事件时 ,访问到的x变量的值为100 。
【示例五】
【原则八】对象终结原则
一个对象的初始化完成Happens-Before于它的finalize()方法的开始 。
例如 ,下面的程序代码 。
【示例六】
运行结果如下所示 。
点击关注 ,第一时间了解华为云新鲜技术~
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!