当前位置: 首页 > news >正文

指令重排序

一、为什么会发生指令重排序?

CPU、编译器不会严格按照你写代码的顺序一行一行执行:

  1. 有些指令阻塞等待(读写内存、IO),CPU 闲着;
  2. 编译器 / CPU 调整指令顺序,把能并行执行的指令提前跑,充分利用硬件资源,提升吞吐。

前提:单线程下,重排不能改变最终执行结果,这是重排序遵守的基础规则(as-if-serial 语义)。

举个简单例子:

int a = 1; int b = 2; int c = a + b;

编译器可以重排成:

int b = 2; int a = 1; int c = a + b;

单线程结果完全一样,无任何影响,所以允许重排。

二、三种重排序来源

1. 编译器优化重排序(JIT 编译器)

JIT 在编译字节码为机器码时,调整语句执行顺序,消除冗余、优化分支、合并读写。

2. CPU 指令重排(处理器乱序执行)

CPU 有多级流水线、多个运算单元。 一条指令读内存很慢,CPU 不会干等,会先执行后面不依赖它数据的指令,也就是乱序执行。

3. 内存系统重排序(缓存、写缓冲区)

每个 CPU 核心有独立高速缓存、写缓冲,写操作不会立刻刷入主存,写入缓冲延时刷新,宏观上看起来读写顺序颠倒。

三、核心规则:as-if-serial 语义

不管怎么重排序,单线程程序的执行结果不能改变。 有数据依赖的两行代码,绝对不能重排:

  • 写后读:a=1; b=a;不能颠倒;
  • 读后写:b=a; a=2;不能颠倒;
  • 写后写:a=1; a=2;不能颠倒。

无数据依赖的代码,才允许随意调换顺序。

四、重排序带来的多线程安全问题(重点考点)

as-if-serial 只保证单线程正确,多线程之间没有这个约束,会出现逻辑错乱,最经典例子:DCL 双重检查锁单例。

instance = new Singleton();

这一行代码底层拆成三步:

  1. 分配一块内存空间;
  2. 在内存中初始化 Singleton 对象(构造函数赋值);
  3. 将 instance 引用指向这块内存地址。

这三步不存在数据依赖,编译器 / CPU 可以重排顺序为:1 → 3 → 2。

多线程故障场景:

  1. 线程 A 执行 instance=new Singleton (),重排执行 1 分配内存、3 赋值引用,还没执行初始化 2
  2. 此时线程 B 进入判断if(instance == null),发现 instance 不为空;
  3. 线程 B 直接使用 instance,拿到一个未初始化完成的半空对象,出现空指针 / 数据错乱。

这就是指令重排序导致的并发 bug,解决方案:用volatile修饰 instance,插入内存屏障禁止这三步重排。

五、volatile 如何禁止指令重排序:内存屏障 Memory Barrier

volatile 会在读写前后插入对应屏障,限制指令上下穿越屏障重排:

  1. volatile 写操作:StoreStore 屏障 + StoreLoad 屏障 所有 volatile 写前面的读写,不能重排到 volatile 写之后; volatile 写不能和后面的 volatile 读重排。
  2. volatile 读操作:LoadLoad 屏障 + LoadStore 屏障 volatile 读后面的读写,不能重排到 volatile 读之前。

简单记: 写 volatile:前面代码不许后移; 读 volatile:后面代码不许前移; 彻底阻断跨 volatile 变量的指令乱序。

六、补充:synchronized 也能防止重排

同步块整体具备原子性、有序性,同步代码内部的指令不会跑到锁外面,锁外代码也不会进入锁内,天然屏蔽跨同步区的重排序。

七、精简总结口述版

指令重排序是 JIT 编译器、CPU、缓存系统为优化性能打乱代码执行顺序;单线程遵循 as-if-serial 语义结果不变,但多线程无保护时会出现逻辑错乱,典型场景是 DCL 单例拿到未初始化对象;volatile 通过插入内存屏障禁止跨 volatile 变量的重排序,保证多线程下执行顺序符合代码书写逻辑。

http://www.jsqmd.com/news/1195925/

相关文章:

  • 手把手教你实现九宫格抽奖特效:前端开发实战指南
  • JAVA基于MC协议实现三菱PLC数据采集与监控
  • Postman便携版:重新定义你的API测试工作流
  • 预算有限怎么做GEO优化?2026高性价比GEO监测工具与入
  • Modbus-RTU协议(从零到一实战篇)
  • 阿里云 RAM 子账号怎么绑定虚拟 MFA?手把手教程
  • 大模型脑机接口:SAE解剖黑盒之谜
  • skill从入门到精通
  • CVA6+ARA通过cheshire平台部署到U280-2
  • 08-LCD1602驱动实战:从时序解析到自定义字符显示
  • day16:极限四则运算法则与复合函数极限
  • 从零搭建企业级静态网页:前端开发者的 MSDN 式指南
  • 2026年度GEO服务商竞争力榜单:服务稳定性与优化效果综合评估
  • 10个即插即用的R语言效率提升技巧
  • AT89S52单片机定时器:从基础结构到精准应用实战
  • 绍兴越城区打井多少钱一米,越城打井电话,老小区水压不够打井 - 瑞溪泉水利
  • 2026年云计算运维实战:Docker与Kubernetes全链路技能指南
  • HarmonyOS7尾部吸附综合页实战:End 吸附策略与多配置综合演示
  • 在线学习平台
  • STM32F103通过USART串口指令实现多LED状态控制
  • FUEL算法调优实战:从参数解析到场景适配的探索效率提升
  • 嵌入式开发中GPMC时序配置详解:从原理到实战
  • IKFast机械臂逆运动学求解器实战:从5微秒响应到MoveIt!插件集成
  • 高级java每日一道面试题-2026年04月10日-实战篇[Docker]-如何排查容器之间的依赖问题?
  • 基于Stable Diffusion的水果艺术风格转换:浮一大果项目实战
  • 2026商用清洁机器人品牌选购指南:主流品牌技术与场景优势深度解析 - 匠言榜单
  • 2026江门本田皓影音响升级观察:江门汇声这套FOCAL劲浪前声场怎么搭?
  • 普尚云课堂 | 射频底层干货:幅度、频率、相位通俗解析一文讲透
  • Hermes Agent 3.x 源码专题【左扬精讲】—— AIAgent 核心循环:消息处理与工具调用
  • 从零到一:使用venv构建Python项目的纯净开发环境