java并发学习--第九章 指令重排序
一、happns-before
happns-before是学习指令重排序前的一个必须了解的知识点,他的作用主要是就是用来判断代码的执行顺序。
1.定义
happens-before是用来指定两个操作之间的执行顺序。提供跨线程的内存可见性。
在java内存模型中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必然存在happens-before关系
2.happens-before规则
a.程序顺序规则
单线程中的每个操作,总是前一个操作happens-before于该线程中的任意后续操作。
简单的说,这个规则就是代码按照顺序执行。
b.监视器规则
对一个锁的解锁,总是happens-before于随后对这个锁的加锁。
这句话可以理解成对于同一把锁,释放与获取是线程可见的;释放锁的操作总是happens-before于获取锁的操作。
c.volatile变量规则
对一个volatile域的写总是happens-before于任意后续对这个volatile域的读。
就是说被volatile修饰的变量,在线程中是是可见的。
d.传递性
这个规则与程序性规则相识:有A、B、C三变量,如果根据程序性规则:
A变量 happens-before B变量,
B变量 happens-before C变量,
则必然有
A变量 happens-before C变量。
f.start规则
这个规则是在多线程场景中会经常出现的:A线程中调用了B线程,那么A线程happens-before于B线程。
或者说A线程的结果对B线程是可见的(结果必须在调用B线程前就已经出现)。
g.join规则
多线程场景中,如果有线程使用了join方法,那么join的线程一点是happens-before于调用的线程。
二、指令重排序
了解过happens-before后,终于可以进入本结的主题了指令重排序。
1.定义
我们知道jvm运行的是java文件编译后的字节码指令,编译器为了优化程序的性能,会重新对字节码指令排序,虽然会重排序,但是指令重排序运行的结果一定是正常的。
2.数据的依赖性
能够进行指令重排序的地方,就是看这个段代码的数据依赖性,有三种情况是不能够进行指令重排序的:
①对某个对象或者变量,先进行赋值再读这个对象的值(写后读)
②对某个对象或者变量,先读这个对象的值再对这个对象赋值(读后写)
③对某个对象或者变量,先后进行了两次赋值(写后写)
如果对这三种情况进行指令重排序的话,获得的结果一定是错误的。所以规定在这三种情况中,是不能进行指令重排序。
3.指令重排序所带来的影响
指令重排序一共分为两种情况:
a.编译器重排序
b.处理器重排序(这个必须是在多个CPU情况下才会发生)
指令重排序在单线程中对我们程序的帮助一定是正向的,它能够很好的优化我们程序的性能。但是在多线程情况下,就不一定了,但是出现指令重排序情况导致线程安全性问题的情况都是很少见的,就算出现也是很难去发现的。最简单的例子:双检查锁单例模式。如果出现了由于指令重排序造成的线程安全性问题,就可以使用volatile关键字来解决。