惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Security @ Cisco Blogs
罗磊的独立博客
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
T
The Blog of Author Tim Ferriss
美团技术团队
T
Tailwind CSS Blog
博客园 - 三生石上(FineUI控件)
博客园 - Franky
G
Google Developers Blog
Jina AI
Jina AI
Stack Overflow Blog
Stack Overflow Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
V
Visual Studio Blog
腾讯CDC
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements
博客园 - 叶小钗
Microsoft Security Blog
Microsoft Security Blog
雷峰网
雷峰网
L
LangChain Blog
Vercel News
Vercel News
Forbes - Security
Forbes - Security
PCI Perspectives
PCI Perspectives
N
News | PayPal Newsroom
S
Security Affairs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
J
Java Code Geeks
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
A
About on SuperTechFans
Attack and Defense Labs
Attack and Defense Labs
Google Online Security Blog
Google Online Security Blog
aimingoo的专栏
aimingoo的专栏
MongoDB | Blog
MongoDB | Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
Cloudbric
Cloudbric
B
Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Proofpoint News Feed
D
DataBreaches.Net
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
B
Blog RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
N
News and Events Feed by Topic

Java

华为的毕昇 JDK 8 AppCDS 特性稳定吗? - V2EX 我把 Java 开发的 kkRepo 支持 AOT 编译, 1s 极速启动,空闲内存小于 200MB - V2EX fastjson1.x 最新版本疑似又出 RCE 漏洞了 - V2EX 讲讲我怎么获得 kkRepo 的首个企业用户,成功替换掉 Nexus,每年节省 $1,620 PRO 订阅费用 - V2EX Sonatype 正引入 Maven Central 仓库发布使用情况可见性和针对高流量发布活动的限制 - V2EX 让 Java 再次伟大,没有人比我更懂得如何打包!(分享一个四两拨千斤的多 jar 打包 exe 方式) - V2EX 使用 kkRepo 搭建 Maven 私服 - V2EX Nexus 的平替 neuxs-plus 开源涉嫌商标侵权,被迫改名 kkRepo - V2EX 我在 IDEA tab 里养了一只猫 - V2EX Java 确实是内存高效的 用 AI 把 visualvm 的部分功能界面换了个马甲 - V2EX javaer 现在出去面试 问哪些内容 sa-token + spring cloud gateway 过滤器顺序问题 - V2EX 关于在 wsl 中使用 idea 进行 Java 开发的问题 - V2EX 求助, Fortify 漏扫问题解决 - V2EX 微信 ClawBot 和企微 AIBot 的 Java SDK - V2EX 老业务信创重构选型大家有啥建议 - V2EX ai 编程的情况在你们使用什么 IDE - V2EX 下面这段 Java 多态的代码,如何才能改为 Go、 Python 、JS、C++版本? - V2EX JDK 26 发布,非 lts 版本 - V2EX DoytoQuery vs SpringDataJPA - V2EX 求推荐比较优秀的 springboot 脚手架项目 - V2EX vscode 里自用 Java 开发插件:支持复制引用路径、rest 接口路径、Arthas 命令等功能 - V2EX 自荐 Java 多线程神器——ThreadForge ,让多线程从此简单 - V2EX 屎山老项目想用 ai 实现自动化测试,有什么可落地的方案么 - V2EX WebFlux 项目 被访问后 访问方关闭连接 无法捕获 SocketException: Connection reset - V2EX 有人用 RUOYI 框架吗? - V2EX 关于商用 jdk 收费的问题,有遥测吗? - V2EX IDEA 在 macOS 上卡住 - V2EX Java : 2026 年 SpringBoot, Quarkus, Solon ? - V2EX 佬们有没有比较好的 Java 脚手架 - V2EX 一个对比 JavaORM 框架的小仓库 - V2EX 想问一个关于 IO 流的细节 - V2EX 关于我在腾讯云 windows 服务器起了一个后端服务,但是服务器本地都请求不到的问题 11 - V2EX 分享一个 idea 的无响应 bug,如果你也遇到 - V2EX JNI 是否有向"简洁"演化的可能? - V2EX 多实例 springboot 微服务重启时 Counter 类型指标重置问题 - V2EX 讨论个问题:该不该重写 equal 和 hashcode - V2EX 求助 泛型转换碰到一个很奇怪的问题 Object 放 Redis 自动变成 JSONObject 了 - V2EX Java Agent 该怎么快速学习?有教程文档推一下吗? - V2EX 能不能别用那烦人的 MyBatis-Plus 了! - V2EX 作为 Java boy 你们业余时间会学点什么? - V2EX 各位 v 友们!有无无敌好用的剪辑软件求推荐! - V2EX 如何从反射的 Field/Method 动态构造 mybatis 的 SFunction? - V2EX Java 得冗余与啰嗦是不是对 AI 不友好 - V2EX 问一个日志不打印栈信息的问题 - V2EX 为何 RestTemplate 无法定制请求 - V2EX 日经吐槽:现在的 Java 语言真的有点令人恶心 - V2EX 新手 javaer 咨询几个问题 - V2EX 自学 Java 看不懂报错信息和代码,有什么推荐 ai 拿来自学的吗? - V2EX 用 netty 编写代理服务器,切换出口 IP,不能及时生效 - V2EX mysql 慢日志分析排查小工具 1.0.0 - V2EX Preferences.userNodeForPackage(Object.class)存储位置在哪里? - V2EX vscode 中 augment code 怎么使用 mcp - V2EX mysql 慢日志分析排查小工具 - V2EX Java 的一次编译到处运行在目前还有优势吗 - V2EX 现在学 Java 算 49 年入国军吗 - V2EX 推广一下自己刚撸的 IDEA 插件—Bean Copy 助手 - V2EX 几千万的数据量,层级权限下的列表展示页查询应该怎么设计? - V2EX 大家生产环境会一直开着 JFR 吗 - V2EX 有没有新版 Java 的佬(jdk25 和 21 这种), 上手还是走官方文档吗 - V2EX Java 程序员面经怎么准备 - V2EX
happens-before 详解 - V2EX
markliu2013 · 2026-07-26 · via Java

很多人在学习 Java 并发的时候,会直接开始背诵知识点:

  • volatile 保证可见性
  • synchronized 保证线程安全
  • Lock 可以手动加锁
  • happens-before 存在八条规则

但绝大多数人都没有弄懂底层根源问题:

  1. 为什么会诞生 happens-before 这套机制?
  2. 为什么恰好是这八条规则,而非十条、二十条?
  3. happens-before 从本质上解决了计算机并发中的什么痛点?

本文站在设计者视角,逐层拆解 Java 内存模型( JMM )中 happens-before 的设计初衷与底层原理。

一、无性能优化的理想环境:根本不需要 happens-before

假设计算机运行环境满足以下理想化条件:

  1. CPU 严格依照代码书写顺序串行执行指令
  2. 不存在 CPU 多级缓存
  3. 编译器不会做任何代码优化
  4. 全程没有指令重排行为
  5. 所有线程直接读写同一份主内存数据

此时多线程交互逻辑十分简单:

  • 线程 A:修改共享变量
  • 线程 B:读取该共享变量

线程 B一定可以读取到线程 A 修改后的最新值,整个程序拥有天然的全局时序,所有操作的先后顺序清晰可追溯。

结论:理想化串行环境下,天然具备有序性与可见性,happens-before 没有存在的必要。

二、真实物理环境:为了性能,引入了三层破坏顺序的优化

现代计算机体系为压榨硬件执行效率,设计了大量性能优化方案,也是并发乱象的源头,主要分为三类。

1. CPU 指令重排

CPU 依靠流水线机制提升运行效率,若死板等待上一条指令执行完毕再执行下一条,会造成大量硬件空闲损耗。 CPU 会在单线程运行结果不变的前提下,自由调换指令执行顺序。

示例代码:

int a = 1;
int b = 2;

CPU 实际执行顺序可能变为:

b = 2;
a = 1;

单线程运行结果无任何差异,CPU 的重排行为是被硬件允许的合法优化。

2. CPU 多级缓存结构

每个 CPU 核心独占独立的高速缓存:L1 Cache 、L2 Cache ,多个核心共享 L3 缓存。 线程修改变量时,数据只会先写入当前核心的私有缓存,不会立刻同步刷新到主内存。 最终现象:线程 A 修改了缓存数据,线程 B 读取主内存只能拿到旧数据,产生数据可见性问题。

3. JIT 编译器优化

Java 运行期 JIT 编译器同样会对字节码做优化处理:

  • 指令重排序
  • 剔除无效冗余代码
  • 将变量缓存至寄存器减少内存访问

编译器只保障单线程执行逻辑正确,无法感知多线程之间的数据依赖关系,多线程场景下优化行为会打乱数据时序。

三、优化带来的恶果:多线程行为不可推理(经典案例)

int a = 0;
boolean flag = false;

// 线程 A 执行逻辑
a = 1;
flag = true;

// 线程 B 执行逻辑
if(flag){
    System.out.println(a);
}

按照常规思维:只要flag = true成立,变量a的值一定等于 1 。 但真实运行环境中,程序大概率会打印出 0,成因分为三点:

  1. CPU/JIT 发生指令重排,线程 A 实际执行顺序变为 flag=true → a=1
  2. 线程 A 的修改仅存于 CPU 私有缓存,未同步到主内存,线程 B 读取旧缓存数据
  3. 线程 B 抢先读取到未更新的原始内存数据

重要说明:该现象不属于 JVM Bug ,是硬件、编译器性能优化带来的必然副作用。

四、JVM 的折中方案:JMM + happens-before

全盘禁用所有软硬件优化,会造成程序性能断崖式下跌;完全放任无限制优化,开发者无法预判多线程代码运行行为。

JVM 需要在运行性能并发正确性之间做平衡,解决方案就是 Java 内存模型( JMM )。 而 happens-before,就是 JMM 交付给开发者、用来约束多线程时序与可见性的标准规则体系。

五、纠正误区:happens-before 不是「代码物理执行先后」

大众普遍错误理解:A happens-before B = A 代码物理时间上一定先执行、B 后执行。

真实定义

happens-before 描述的是数据可见性与操作因果关系,而非真实的 CPU 执行时序:

若 A happens-before B ,JVM 强制保证:B 一定能观测到 A 执行完成后的所有数据修改,A 的操作结果会对 B 产生有效影响。

以 volatile 读写举例:

// volatile 写
flag = true;
// volatile 读
if(flag)

并不是 CPU 必须先执行写操作、再执行读操作;真实含义为:线程一旦读取到flag=true这个 volatile 标记,写操作之前所有变量的修改,必须对当前读线程全部可见。

六、happens-before 为什么刚好是八条规则?

规则数量并不是官方凭空指定为 8 条,本质是:Java 语言中所有合法的线程同步、通信方式,对应一条 happens-before 约束,八条规则是对全部同步行为的标准化数学描述。

八条完整规则明细:

  1. 程序次序规则:同一个线程内,书写在前的操作 happens-before 书写在后的操作。
  2. 监视器锁规则:同一个锁,锁的释放操作 happens-before 后续该锁的获取操作。
  3. volatile 变量规则:volatile 字段的写入操作 happens-before 后续对该字段的读取操作。
  4. 线程启动规则Thread.start()方法调用之前的所有代码,happens-before 新启动线程内部的任意操作。
  5. 线程终止规则:线程内部所有执行操作,happens-before 其他线程执行join()方法并正常返回。
  6. 线程中断规则:调用interrupt()发起中断的操作,happens-before 被中断线程检测到中断标识的操作。
  7. final 字段规则:对象构造方法完成初始化后,final 修饰字段的值对其他访问线程永久可见,实现对象安全发布。
  8. 传递性规则:若 A happens-before B ,B happens-before C ,则推导得出 A happens-before C 。

七、传递性:happens-before 体系的核心压缩机制

如果没有传递性规则,系统需要为每两段存在先后关系的操作单独定义约束,规则数量会无限膨胀。

举例链路:A 修改数据 → B 释放锁 → C 获取锁 依靠传递性,自动建立 A→B→C 的 happens-before 关系,A 的数据修改天然对 C 可见;无需单独定义 A 与 C 的绑定规则。

传递性是整个 happens-before 体系的精简压缩核心,用最少规则覆盖全部时序链路。

八、happens-before 的设计目标:构建最小完备系统

JMM 设计 happens-before 时锁定三大核心目标:

  1. 完备性:全覆盖 Java 所有线程同步场景,不存在遗漏的同步关系
  2. 简洁性:控制规则数量,降低开发者学习与使用成本
  3. 兼容性:最大限度放行 CPU 、编译器的各类优化,不牺牲程序运行性能

本质:happens-before 是一套最小且自洽的多线程行为推理系统

九、并发问题的根源不是重排/缓存,而是数据竞争

指令重排、多级缓存、编译器优化只是并发异常的外在表现,真正的元凶是数据竞争( Data Race )

数据竞争判定三要素(同时满足即产生数据竞争)

  1. 多个线程访问同一个共享变量
  2. 至少有一个线程对变量执行写入操作
  3. 读写线程之间不存在任何 happens-before 约束

示例(无同步自增):

int count = 0;
// 线程 A
count++;
// 线程 B
count++;

无锁、无 volatile 、无任何同步措施,两条自增操作无因果绑定,程序最终结果不可预测。

十、JMM 核心保障原则:DRF-SC

DRF-SC 全称:Data Race Free → Sequential Consistency 翻译:无数据竞争的程序,执行效果等价于顺序一致性执行

通俗解释:开发者正确使用同步机制、依靠 happens-before 消除数据竞争后,多线程代码的运行逻辑和单线程代码一样具备稳定、可预期的执行结果。 反之:代码存在数据竞争时,JVM 不会对程序运行行为做任何承诺,运行结果随机不可控。

十一、Java 并发知识整体链路梳理

graph TD
A[CPU 为性能引入乱序执行+私有缓存] --> B[JVM 开放编译器、指令优化权限]
B --> C[多线程读写共享变量极易产生数据竞争]
C --> D[解决方案:使用同步机制建立线程时序约束]
D --> E[各类同步行为映射为 happens-before 关系]
E --> F[happens-before 统一保障数据可见性、操作有序性]
F --> G[无数据竞争 → 程序行为等同于顺序执行]

十二、全文总结

一句话概括 happens-before 的诞生意义:

现代硬件为性能打破了原始的串行执行模型,happens-before 是 Java 设计的一套折中数学模型:既允许软硬件正常优化保障性能,又给开发者提供了可推理、可管控的多线程并发边界。

CPU 重排、缓存不一致、编译器优化都不是并发 Bug 的本源问题,线程之间是否通过同步机制建立合法的 happens-before 因果关系,才是解决并发安全的核心关键。 happens-before 就是 Java 体系中,定义线程数据因果关系的标准数学模型。