Java多线程环境下可能会出现指令重排的代码示例
什么是指令重排?
我们在执行程序时,为了提高性能,编译器和处理器某些情况下会对指令进行重排序。
as-if-serial语义
不管怎么重排序,不能影响单线程环境下的执行结果,这是as-if-serial语义定义的,编译器和处理器阶段的重排都遵循该规则。
编译器和处理器都遵循的指令重排的原则
a、数据依赖,只对不存在数据依赖的指令进行重排。
b、控制依赖,允许对有控制依赖关系的指令做重排。
Java多线程环境下可能会出现指令重排的代码示例
既然有了上面两个指令重排的前提,我们就可以根据这些规则写一个demo来实际测验出指令重排。
这个示例里,两个线程无论谁先执行,m和n都不可能都为0,如果出现了,就代表发生了指令重排
public class SysParamEnumController { int a = 0; int b = 0; int m = 0; int n = 0; public static void main(String[] args) throws InterruptedException { SysParamEnumController sysParamEnumController = new SysParamEnumController(); while (true){ Thread t1 = new Thread(() -> { sysParamEnumController.a = 1; sysParamEnumController.m = sysParamEnumController.b; }); Thread t2 = new Thread(() -> { sysParamEnumController.b = 2; sysParamEnumController.n = sysParamEnumController.a; }); t1.start(); t2.start(); t1.join(); t2.join(); if (sysParamEnumController.m == 0 && sysParamEnumController.n == 0) { System.out.println("m和n都为0,代表出现了指令重排"); break; } sysParamEnumController.a = 0; sysParamEnumController.b = 0; sysParamEnumController.m = 0; sysParamEnumController.n = 0; } } }
结果
循环结束就代表受到了指令重排的影响,因为指令重排只会小概率出现,所以每次跑的时间都会不一样。
如何避免指令重排?
使用内存屏障(volatile)或临界区(synchronized)
对应代码实现分别是给a和b加上volatile
volatile int a = 0; volatile int b = 0;
volatile的规则
1、第一个操作是volatile读时,第二个无论什么操作都不能重排
2、第二个操作是volatile写时,第一个无论什么操作都不能重排
3、第一个操作是volatile写时,第二个是volatile读时不能重排
或者给两个线程里的逻辑加上synchronized
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2017-07-27 设计模式之状态模式学习理解
2017-07-27 java并发之ReentrantLock学习理解
2017-07-27 java并发之读写锁ReentrantReadWriteLock的使用