JMM-有序性
文章目录
1.概念
JVM 会在不影响正确性的前提下,可以调整语句的执行顺序,思考下面一段代码。
可以看到,至于是先执行 i 还是 先执行 j ,对最终的结果不会产生影响。所以,上面代码真正执行时,既可以是
或者
这种特性称之为『指令重排』。
多线程下『指令重排』会影响正确性
2.指令重排原理
2.1 关于CPU的几个概念
-
Clock Cycle Time
主频的概念大家接触的比较多,而 CPU 的 Clock Cycle Time(时钟周期时间),等于主频的倒数,意思是 CPU 能够识别的最小时间单位,比如说 4G 主频的 CPU 的 Clock Cycle Time 就是 0.25 ns,作为对比,我们墙上挂钟的Cycle Time 是 1s。
例如,运行一条加法指令一般需要一个时钟周期时间 -
CPI
有的指令需要更多的时钟周期时间,所以引出了 CPI (Cycles Per Instruction)指令平均时钟周期数 -
IPC
IPC(Instruction Per Clock Cycle) 即 CPI 的倒数,表示每个时钟周期能够运行的指令数 -
CPU 执行时间
程序的 CPU 执行时间,即我们前面提到的 user + system 时间,可以用下面的公式来表示:
程序 CPU 执行时间 = 指令数 * CPI * Clock Cycle Time
2.2 指令重排优化
事实上,现代处理器会设计为一个时钟周期完成一条执行时间最长的 CPU 指令。为什么这么做呢?可以想到指令还可以再划分成一个个更小的阶段,例如,每条指令都可以分为:
【取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回】
这 5 个阶段
在不改变程序结果的前提下,这些指令的各个阶段可以通过重排序和组合来实现指令级并行。指令重排的前提是,重排指令不能影响结果,例如
2.3 支持流水线的处理器
现代 CPU 支持多级指令流水线,例如支持同时执行 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 的处理器,就可以称之为五级指令流水线。
这时 CPU 可以在一个时钟周期内,同时运行五条指令的不同阶段(相当于一条执行时间最长的复杂指令),IPC = 1,
本质上,流水线技术并不能缩短单条指令的执行时间,但它变相地提高了指令地吞吐率。
2.4指令重排导致的问题
【情况1】先执行线程1,再执行线程2,r1=1
【情况2】先执行线程2,再执行线程1,r1=4
【情况3】先执行2,但发生指令重排,先执行ready=true,r1=0
情况3可以使用并发压测工具复现:
借助 java 并发压测工具 jcstress
https://wiki.openjdk.java.net/display/CodeTools/jcstress
mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=org.openjdk.jcstress -
DarchetypeArtifactId=jcstress-java-test-archetype -DarchetypeVersion=0.5 -DgroupId=cn.itcast -
DartifactId=ordering -Dversion=1.0