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

你真的理解 volatile 关键字了吗?

你真的理解 volatile 关键字了吗?

volatile 不只是"线程可见"。这篇文章用几个具体的 case,把它在 JMM 里的工作方式说清楚。


一、从最简单的 volatile 开始

volatile int flag = 0;// 线程 A
flag = 1;// 线程 B
if (flag == 1) {// ...
}

volatile 做了什么?

建立 happens-before:

  • 线程 A 对 flag 的写 happens-before 线程 B 对 flag 的读

还有一条容易忽略的规则:

volatile 写之前的所有普通写,在 volatile 读之后对读线程可见


二、volatile 不是"广播",而是"同步点"

一个常见的误解是:只要加了 volatile,其他线程就会自动看到变化。

这是错的。

Case 1:写了 volatile,但没读

int A = 0;
volatile int B = 0;// 线程 A
A = 1;
B = 1;// 线程 B
int x = A;   // 能看到 1 吗?

结论:❌ 不能保证

volatile 的 happens-before 只在"读发生时"生效。线程 B 没有读 B,没有同步点,就没有可见性保证。

volatile 是拉模型,不是推模型。


三、volatile + 普通变量的正确发布模式

Case 2:volatile 作用的传递性

线程 B 能正确读到 A 吗?

int A = 0;
volatile boolean ready = false;// 线程 A
A = 1;
ready = true;// 线程 B
if (ready) {System.out.println(A);
}

结论:✅ A 一定是 1

原因是 happens-before 的传递性:

如果 A happens-before B,B happens-before C
那么 A happens-before C

链路完整:

线程 a:
A = 1↓  (线程内顺序)
B = 1  (volatile 写)↓  (volatile 写-读)
线程 b:
if (B == 1)↓  (传递性)
读取 A == 1

这是 JMM 官方认可的发布-订阅模式:

// producer
data = ...; // 普通写
ready = true; // volatile 写// consumer
if (ready) { // volatile 读use(data); // 安全
}

四、多个 volatile 变量 ≠ 自动传递

Case 3:A 普通,B / C 是 volatile

int A = 0;
volatile int B = 0;
volatile int C = 0;// 线程 A
A = 1;
B = 1;// 线程 B
int c = C;
int a = A;

结论:❌ 不能保证看到 A = 1

volatile 的 happens-before 只对同一个 volatile 变量生效:

  • 写 B → 读 B:有保证
  • 写 B → 读 C:没有任何语义关系

volatile 不会跨变量广播内存可见性。


五、对象字段里有 volatile,就能"顺带同步"吗?

Case 4:对象里 ABC,只有 C 是 volatile

class Obj {int A;int B;volatile int C;
}// 线程 A
obj.A = 1;
obj.B = 2;
obj.C = 3;// 线程 B
int a = obj.A;
int b = obj.B;

结论:❌ 不能保证

volatile 是变量级别的,不是对象级别的。不读 C,就不会触发 volatile 读,A / B 仍然是普通读。


六、"我读了 volatile,但没用它",算吗?

Case 5:返回 volatile,但返回值没用

volatile int C;int foo() {return C;
}// 线程 B
foo();  // 返回值未使用

结论:⚠️ 不一定算

语义上确实是一次 volatile 读,但如果返回值没被使用,JIT 有权把这次读整个消掉。

两个层面分开看:

① Java 语言语义层面

return C; 在规范里就是一条 volatile 读指令,没有争议。

② JVM / JIT 实际执行层面

JMM 允许 JVM 这么做:

如果一个 volatile 读的结果对程序行为没有任何影响,JVM 可以消除这个读。

这个返回值没有赋给变量、没有用于条件判断、没有参与计算,对可观察行为没有影响,JVM 会把它当死代码处理。

需要澄清一点:

volatile 的内存语义,不是 Java 语言层面的可观察副作用。

volatile 只约束重排序与可见性,不算 I/O、不算异常、不算锁,除非它被实际执行,否则不是 happens-before 的强制保留点。


七、什么时候 volatile 读一定成立?

Case 6:volatile 参与了判断

volatile int C;boolean foo() {return C == 0;
}

结论:✅ 一定是 volatile 读,不可消除

return C == 0; 和上面的 return C; 区别在于:这里读取 C 的结果直接参与了比较,影响了方法返回值。

方法返回值是可观察行为,JVM 没有任何空间把它优化掉。


八、volatile 的本质

可以把 volatile 理解为:

  • 写 volatile = release
  • 读 volatile = acquire

它同步的不是"某个变量",而是一个时间点:

在这个点之前,数据已经写完了


九、volatile 在 Disruptor 中的应用

用 Disruptor 来验证一下这个模型。

很多人用 Disruptor 时有这个疑惑:Event 字段没有 volatile,生产者写完数据,消费者却总能看到最新值。

原因是:Disruptor 同步的不是数据,而是"数据写完"这件事。

核心结构

Event 普通字段↑
Sequence(volatile)

生产者逻辑(简化)

Event e = ringBuffer.get(seq);
e.x = ...;
e.y = ...;
sequence.set(seq); // volatile 写(发布)

消费者逻辑

long s = sequence.get(); // volatile 读
Event e = ringBuffer.get(s);

happens-before 链

写 Event 字段↓
volatile 写 sequence↓ happens-before
volatile 读 sequence↓
读 Event 字段

和前面的发布-订阅模式一模一样。

Event 字段不需要 volatile,因为它不是并发访问,而是被发布的快照。一个 event slot 只被一个生产者写,之后只被一个消费者读,靠 Sequence 做交接。这是所有权转移,不是共享访问。

如果 Event 字段都是 volatile:每个字段访问都有内存屏障,cache line 来回抖,false sharing 风险暴涨。

Disruptor 用一次 volatile 发布整个对象,相当于一次内存批量提交,比字段粒度的 volatile 快得多。


十、小结

  1. volatile 不是"自动可见",而是同步点
  2. 不读 volatile,就没有 happens-before
  3. volatile 不会跨变量传播可见性
  4. 可能被优化掉的 volatile 读,不是同步点
  5. 高性能无锁结构,都是"数据 + volatile 发布点"

写在最后

volatile 在 JMM 里的行为比大多数人理解的要复杂。不是"加了就有可见性",而是"读的那一刻建立了同步"。把这个模型理解清楚,写并发代码时才能真正知道自己在保证什么。

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

相关文章:

  • Spring Boot 3 全局异常处理终极指南(附完整代码架构),拿走即用
  • 如何摆脱游戏卡顿困扰:DLSS Swapper的智能性能管理方案
  • 为什么FreeBSD和苹果都爱用Clang?聊聊它的模块化设计与商业友好性
  • 优雅进程终止:Go工具halt的设计原理与实战应用
  • 泉州 CPPM 认证培训 福建制造业采购必考证书 - 中供国培
  • 全屋定制酒柜技术拆解:从板材到工艺的硬核标准 - 奔跑123
  • Linux 端口管理指南
  • 当用户觉得 Agent 变笨时,真正退化的往往不是模型
  • 大模型小白入门指南:3分钟读懂核心逻辑+高性价比产品推荐(建议收藏+转发)
  • 2026年OpenAI API聚合站权威推荐:为开发者与企业提供全方位的可靠选型指南
  • 人工智能生成内容的文化影响:第一部分
  • 【权威实测】Perplexity UI v2.8.3组件查询API响应延迟骤降76%的6项必调参数
  • Rust 实现轻量级终端复用器 Kibitz:零配置的会话管理利器
  • 2026 年 5 月常州劳力士欧米茄浪琴市场行情对比 - 奢侈品回收测评
  • 2026 济南黄金回收高价变现攻略|拿捏出手时机,多赚不少钱 - 奢侈品回收测评
  • C# —— 上位机行业解析与完整学习规划
  • 别盲目跟风!程序员转智能体开发,先看这篇避坑指南
  • 收藏!小白程序员必看:AI抢工作?2026年高薪新职业已出现!速进!
  • 最近,程序员的离职潮彻底消失了。。。
  • ResearcherSkill项目:构建高效可复现的科研工作流与知识管理体系
  • 基于Astro与TypeScript构建实时星图:技术架构与天文数据可视化实践
  • 全屋定制技术拆解:从板材到售后的硬核选型逻辑 - 奔跑123
  • 嘉兴 CPPM 报考流程 在职采购业余取证方案(浙江官方授权中心) - 中供国培
  • 国内专业刺绣章定制企业排行:实力厂商综合一览 - 奔跑123
  • 本地化AI编程副驾驶jaipilot-cli:终端集成与高效开发实践
  • 3步解锁小爱音箱隐藏技能:用xiaomusic打造专属智能音乐中心
  • CatchClaw爬虫框架:从零构建高效异步网络爬虫的实践指南
  • 矩阵融合,跨镜永续追踪三维重构,跨镜轨迹锁定
  • 江西德兴市发布严打烟花违法通告(地方政策)
  • 2026年在线脑图工具深度横评:7款主流平台技术向实测与选型建议