今天来继续总结JMM的其他相关知识,通过上一篇文章我们已经大概知道了多线程情况下某些程序存在可见性问题。什么是可见性
定义是:当一个线程修改了共享变量的值,其他线程能够看到修改的值。
我们都知道java程序运行时,数据都存储在java运行时数据区(包括,虚拟机栈,方法区,堆,本地方法区,程序计数器等),而我们的可见性问题主要存在于堆。临时变量,或者静态变量,都存在方法区和栈之中都由各个线程独有,并不存在可见性问题。那什么是JMM呢? 引入《java并发编程艺术》的一句话:JMM定义了线程(Local Memory)和主内存(Main Memory)之间的抽象关系。我们现在使用的是JSR-133内存模型。内容比较多大家有兴趣可以自己去看http://ifeve.com/wpcontent/uploads/2014/03/JSR133%E4%B8%AD%E6%96%87%E7%89%88.pdf
那下面我就来介绍其中的主要内容:
happens-before
jsr-133使用happens-before来描述两个操作之间的可见性。如果一个操作的结果对另一个操作可见,那么两个操作一定存在happens-before关系。其中我们日常能够用到的是
1.程序顺序规则:一个线程中的操作,happens-before于该线程中的任意后续操作。
2.volatie规则:对一个volatile变量的写操作,happens-before于后续对这个volatie变量的读操作。
3.Object-Monitor 规则:对一个锁的解锁,happens-before 于后续对这个锁的加锁。
4.传递性: A happens-before B B happens-before C 那么 A happens-before C 。
(这里要注意 只是后一个操作需要得到前一个操作的结果,但是并不代表执行顺序)
(该图来自《java并发编程艺术》)
记住happens-before 原则可以让我们不用去理解各种底层的重排序规则,让我们可以直接关注业务。
happens-before模型有个最大的缺点会无中生有(Out Of Thin Air),所以我们的JMM还定义了一些其他规则:对于未同步或未正确同步的多线程程序,JMM只提供最小安全性:线程执行时读取的值,要么是之前某个线程写入的值,要么是默认值(0,null,false)所以jvm在分配对象时,首先会对内存空间进行清零,然后才在其上分配对象。
此外JSR-133还增加了对final关键字的一些要求:
1.在构造函数内对一个final域的写入,于随后把这个被构造对象的引用赋值给另一个引用变量,这两个操作之间不可重排序
2.初次读一个包含final域的对象的引用,与随后读这个final域,这两个操作不能重排序。
至于以上的实现原理,都是使用内存屏障。JMM的总结就先到这里。
总结就到这里欢迎大家指正问题 !! 与君共勉