有序性
jvm在不影响正确性的前提下,可以调整语句的执行顺序–》【指令重排】
多线程下【指令重排】会影响正确性,指令重排是JIT编译器在运行时的一些优化,该现象需要大量测试才能复现
static int i;
static int j;
// 在某个线程内执行如下赋值操作
i = ...;
j = ...;
至于是先执行 i 还是 先执行 j ,对最终的结果不会产生影响。所以,上面代码真正执行时,既可以是
i = ...;
j = ...;
也可以是
j = ...;
i = ...;
指令重排优化
事实上,现代处理器会设计为一个时钟周期完成一条执行时间最长的CPU指令,指令还可以再划分为一个个更小的阶段,例如把每条指令划分为:取指令——指令译码——执行指令——内存访问——数据写回
在不改变程序结果的前提下,这些指令的各个阶段可以通过重排序和组合来实现指令级并行
指令重排的前提是重排指令不能影响结果
// 可以重排的例子
int a = 10; // 指令1
int b = 20; // 指令2
System.out.println( a + b );
// 不能重排的例子
int a = 10; // 指令1
int b = a - 5; // 指令2
支持流水线的处理器
cpu可以在一个时钟周期内同时运行多条指令的不同阶段(其实相当于一条执行时间最长的复杂指令),本质上流水线技术并不能缩短单条指令的执行时间,但是变相提高了指令的吞吐率
解决指令重排问题
volatile修饰的变量可以禁用指令重排
@JCStressTest
@Outcome(id = {"1", "4"}, expect = Expect.ACCEPTABLE, desc = "ok")
@Outcome(id = "0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "!!!!")
@State
public class ConcurrencyTest {
int num = 0;
volatile boolean ready = false;
@Actor
public void actor1(I_Result r) {
if(ready) {
r.r1 = num + num;
} else {
r.r1 = 1;
}
}
@Actor
public void actor2(I_Result r) {
num = 2;
ready = true;
}
}