指令重排序是在不破坏单线程语义前提下的性能优化行为,仅在操作间无数据依赖(RAW/WAR/WAW)时发生;编译器重排发生在编译期,处理器重排发生在执行期,均需遵守as-if-serial语义,且volatile、synchronized等同步机制通过内存屏障禁止越界重排。

指令重排序不是“乱来”,而是在明确约束下发生的性能优化行为。它只在**不破坏单线程语义**的前提下发生,核心条件是:两条操作之间**不存在数据依赖**。
编译器层面的重排序发生条件
编译器(包括 javac 和 JIT)会在生成字节码或机器码时调整语句顺序,前提是:
- 操作之间没有真依赖、反向依赖或读后写依赖——比如
a = 1;和b = 2;可互换,但a = 1;和c = a + 3;不可交换; - 重排后,单线程执行结果与原顺序完全一致(as-if-serial 语义);
- 常见触发场景包括:循环不变量外提、公共子表达式消除、方法内联后跨方法重排、无副作用的初始化语句挪动。
处理器层面的重排序发生条件
CPU 在运行时做动态调度,重排序实际发生在指令执行阶段,需同时满足:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 指令间无数据依赖(RAW、WAR、WAW),且不争抢同一执行单元(如 ALU 或浮点单元);
- 写操作进入 store buffer 后未立即刷入缓存,读操作可能从旧缓存中取值;
- 失效队列(invalidate queue)延迟处理 MESI 协议消息,导致其他核看到过期数据;
- 分支预测成功时,预执行路径中的指令可能提前完成,再根据真实分支结果提交或丢弃。
哪些情况一定不会重排序
只要存在以下任一关系,编译器和 CPU 都必须保持原始顺序:
立即学习“Java免费学习笔记(深入)”;
-
写后读(RAW):如
x = 1; y = x;,第二句依赖第一句结果; -
写后写(WAW):如
a = 1; a = 2;,最终值必须是 2; -
读后写(WAR):如
t = a; a = 1;,t 必须拿到旧值; - 显式同步机制介入处:volatile 写/读、synchronized 进入/退出、Lock 加锁/解锁,都会插入内存屏障,切断重排边界。
多线程问题的本质来源
单线程下重排序永远安全,因为 as-if-serial 保证了结果一致性;但多线程共享变量时,不同线程观察到的操作顺序可能不一致——线程 A 看到 flag = true 先于 a = 1,线程 B 却基于 flag 为 true 去读 a,结果读到 0。这不是 bug,而是缺乏 happens-before 关系导致的合法现象。

















