Java重排序和happens-before学习思路
Fri, Apr 1, 2016 标签: Java鉴于在互联网上大家资料抄袭严重,本文主要介绍学习Java中的重排序和happens-before关系等相关概念的学习思路。建议大家看书系统学习。
我觉得理解这些概念,比较好的思路是这样的:
知道什么是重排序? > 重排序是编译器和处理器为了优化性能而对指令执行顺序进行重新排序。
现在你已经知道了什么是重排序,认识下重排序的发生位置:
- 编译器重排序
- 指令级并行重排序
- 内存系统重排序
理解单线程程序中可能发生指令的重排序
这就是说在单线程中,编译器也会进行重排序。但是为什么我以前写了这么久单线程的Java程序,都没有发现指令重排序这个问题?
原因是java遵从as-if-serial语义,即单线程的程序即使发生了重排序,程序的执行结果不能被改变。所以,即使发生了重排序,执行结果是一样的,所以你感觉不到。
比如说,对于有数据依赖的语句进行重排序,就会改变结果,所以会禁止此种情况的重排序。
理解happens-before的定义
- 如果一个操作happens-before另一个操作,那么第一个操作的执行结果对第二个操作可见。且第一个操作排在第二个操作之前。
- 两个操作之间存在happens-before关系,如果重排序之后的对执行结果没有影响,也可以重排序。即表明不一定存在这个关系就一定不能重排序。
happens-before规则
这里有6条:程序次序规则;监视器锁规则;volatile规则;传递性规则等等。 比如:程序次序规则,它要求一个线程中的每个操作,happens-before该线程的任何后续操作。 这里大家去查阅详细资料。
此时可能会有一个疑惑,既然多线程程序的先后语句满足happens-before规则中程序次序规则,为什么还会重排序?
其实这个问题前面已经说了,第4点,在多线程程序中,也是由多个单线程组成的。他们中的每一个线程前后是存在happens-before规则。java以单线程的角度来看待这个问题:java认为对线程中语句进行重排序是不会改变结果的。
因为java又不知道你这个代码是会在多线程里运行还是单线程里运行,它只保证单线程运行时,重排序后不改变执行结果,当这代码在多线程里运行时就可能出现问题。
所以我们就需要在多线程中进行正确的同步的一个原因。
学习volatile语义、锁的语义、和final的语义
此时就可惜学习java中的Thread的实现和并发容器的实现
总结
线程一:a -> b -> c 线程二:A -> B -> C
若没有重排序的结果可能是
a -> A -> B -> b -> c -> C
也就是单个线程的顺序不变
若发生了重排序结果可能是
b -> B -> A -> a -> c -> C
就要分析里面的代码是否有happens-before的关系以及对每个单线程而言的重拍序是否会改变运行结果来分析。
本文参考:《并发编写的艺术》
欢迎批评指正:xiegeixiong@gmail.com