JUC并发编程 共享模式之内存 有序性 -- 有序性(指令重排序优化 & 支持流水线的处理器 & 指令重排序的问题/验证/禁用)

1. 有序性

JVM 会在不影响正确性的前提下,可以调整语句的执行顺序,思考下面一段代码:

static int i;
static int j;
// 在某个线程内执行如下赋值操作
i = ...; 
j = ...;

可以看到,至于是先执行 i 还是 先执行 j ,对最终的结果不会产生影响。所以,上面代码真正执行时,既可以是:

i = ...; 
j = ...;

也可以是:

j = ...;
i = ...;

这种特性称之为『指令重排』,多线程下『指令重排』会影响正确性。为什么要有重排指令这项优化呢?从 CPU执行指令的原理来理解一下吧


2. 指令重排序优化

事实上,现代处理器会设计为一个时钟周期完成一条执行时间最长的 CPU 指令。为什么这么做呢?可以想到指令还可以再划分成一个个更小的阶段,例如,每条指令都可以分为: 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 这 5 个阶段
在这里插入图片描述

在不改变程序结果的前提下,这些指令的各个阶段可以通过重排序和组合来实现指令级并行,这一技术在 80’s 中叶到 90’s 中叶占据了计算架构的重要地位。

提示:

分阶段,分工是提升效率的关键!

指令重排的前提是,重排指令不能影响结果,例如:

// 可以重排的例子
int a = 10; // 指令1
int b = 20; // 指令2
System.out.println( a + b );
// 不能重排的例子
int a = 10; // 指令1
int b = a - 5; // 指令2

3. 支持流水线的处理器

现代 CPU 支持多级指令流水线,例如支持同时执行 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 的处理器,就可以称之为五级指令流水线。这时 CPU 可以在一个时钟周期内,同时运行五条指令的不同阶段(相当于一条执行时间最长的复杂指令),IPC = 1,本质上,流水线技术并不能缩短单条指令的执行时间,但它变相地提高了指令地吞吐率。
在这里插入图片描述


4. 指令重排序的问题/验证

int num = 0;
boolean ready = false;
// 线程1 执行此方法
public void actor1(I_Result r) {
 if(ready) {
 r.r1 = num + num;
 } else {
 r.r1 = 1;
 }
}
// 线程2 执行此方法
public void actor2(I_Result r) { 
 num = 2;
 ready = true; 
}

I_Result 是一个对象,有一个属性 r1 用来保存结果,问,可能的结果有几种?有同学这么分析:

  • 情况1:线程1 先执行,这时 ready = false,所以进入 else 分支结果为 1

  • 情况2:线程2 先执行 num = 2,但没来得及执行 ready = true,线程1 执行,还是进入 else 分支,结果为1

  • 情况3:线程2 执行到 ready = true,线程1 执行,这回进入 if 分支,结果为 4(因为 num 已经执行过了)

  • 但我告诉你,结果还有可能是 0 😁😁😁,信不信吧!这种情况下是:线程2 执行 ready = true,切换到线程1,进入 if 分支,相加为 0,再切回线程2 执行 num = 2,相信很多人已经晕了 😵😵😵

这种现象叫做指令重排,是 JIT 编译器在运行时的一些优化,这个现象需要通过大量测试才能复现:

测试结果:

在这里插入图片描述
可以看到,出现结果为 0 的情况有 1568 次,虽然次数相对很少,但毕竟是出现了。


5. 指令重排序禁用–volatile

volatile 修饰的变量,可以禁用指令重排,使用这个变量之前的代码,可以保证指令顺序的正确性。

示例代码:

int num = 0;
volatile boolean ready = false;
// 线程1 执行此方法
public void actor1(I_Result r) {
 if(ready) {
 r.r1 = num + num;
 } else {
 r.r1 = 1;
 }
}
// 线程2 执行此方法
public void actor2(I_Result r) { 
 num = 2;
 ready = true; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeJiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值