← [ BACK_TO_NOTES ]

Java 内存模型与 happens-before

JMM 与 happens-before:何时一次写入对其它线程可见,同步动作如何建立顺序。

#concept #growing #dev #java

[!info] related notes

Java 内存模型与 happens-before

这个概念为什么出现

并发 bug 常不是“没想到交错”,而是没有同步却假设看见最新值。Java 内存模型(JMM)定义:在何种同步关系下,线程 A 的写对线程 B 的读可见且有序。核心工具概念是 happens-before。

[!abstract] 一句话理解 happens-before 约束哪些执行结果允许被观察,并不要求物理上禁止一切重排。常见关系包括同一锁的释放与后续获取、同一 volatile 的写与后续读,以及线程启动与结束检测。

最小可观察示例

场景:发布不可变配置快照

public class ConfigPublish {
    static class Cfg {
        final int port;
        Cfg(int port) { this.port = port; }
    }
    private volatile Cfg ready; // 用 volatile 发布引用

    void publish(int port) {
        Cfg c = new Cfg(port); // 先构造完成
        ready = c;             // 发布:写 volatile
    }

    int readPort() {
        Cfg c = ready;         // 读 volatile
        return c == null ? -1 : c.port;
    }

    public static void main(String[] args) throws Exception {
        ConfigPublish p = new ConfigPublish();
        Thread writer = new Thread(() -> p.publish(8080));
        writer.start();
        writer.join(); // join 建立 happens-before
        System.out.println(p.readPort());
    }
}

建议运行:

javac ConfigPublish.java && java ConfigPublish

期望输出:

8080

这个程序同时用了 volatile 与成功 join;即使去掉 volatile,主线程也会因 join 获得可见性,所以它不能单独证明 volatile 的必要性。配置还用了 final 字段,其初始化安全性另有规则。

结合场景再看三个关注点

  1. 安全发布对象构造完成后再发布引用。
  2. volatile/join/锁是规范层工具,不是“睡眠碰运气”。
  3. 没有同步的共享可变属于数据竞争,行为可极难推理。

核心概念与准确模型

1. happens-before 常见来源(直觉列表)

  • 程序次序(单线程内)
  • 监视器锁:unlock → 后续 lock 同一锁
  • volatile:写 → 后续读同一变量
  • 线程 start:start → 新线程中的动作
  • 线程终止检测:线程全部动作 → 另一线程通过 join 成功确认终止后的动作;带超时的 join 若超时返回,不代表线程已结束。\
  • 传递性

2. 数据竞争

无 happens-before 关系的冲突读写 = 数据竞争;正确程序应避免。

3. 规范 vs 硬件

  • 用规范推理正确性
  • CPU/缓存是实现与直觉辅助,不能替代规范

边界情况与反直觉行为

  1. 双重检查锁定必须正确使用 volatile(经典题)。
  2. 正确构造且构造期间不逸出 this 的对象,其 final 字段有专门的初始化安全保证;这不让所有可变字段自动线程安全。\
  3. 单测“偶尔通过”不能证明无数据竞争。

常见误区

[!warning] 常见误区:加几个 sleep 让并发测试变绿 那是掩盖竞态;应建立同步与用并发测试工具。

工程实践

  1. 优先不可变 + 消息传递缩小共享。
  2. 共享可变必须能指出 happens-before 边。
  3. 用 jdk.jfr / 线程转储 / 并发 TLA 级推理(进阶)。
  4. 读 JCIP 与 JLS/JMM 章节交叉验证。

本节总结

  • JMM 回答可见性与有序
  • happens-before 是推理货币
  • 无同步共享可变即风险

自测题

  1. 为什么 unlock 对后续 lock 重要?
  2. join 提供什么保证?
参考答案
  1. 释放锁 happens-before 后续获取同一锁,约束后续读取能观察到的结果;“刷新缓存”只是实现类比。\
  2. 成功确认目标线程终止后,目标线程的动作对后续操作可见;超时返回或因中断抛异常不自动提供同一终止保证。

延伸阅读与资料来源

资料类型支撑内容
JLS Chapter 17 Threads and Locks规范JMM
JSR 133 FAQ经典 FAQ直觉问答
创建于 2026/7/15 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本