`volatile` 到底保证了什么:可见性、有序性,和它管不到的原子性
前言有个 bug 特别典型一个线程把开关flag改成了true另一个线程里while (!flag)的循环却像没听见一样一直转个不停就是不退出。把flag加上volatile问题立刻消失。于是很多人得出一个模糊的结论“volatile就是个轻量级的synchronized加上就线程安全了。”这个理解害人不浅。因为紧接着他们会写出volatile int count; count;这种代码然后在多线程下发现count的最终结果总是对不上——明明加了volatile怎么还是不安全真相是volatile只保证两件事——可见性和有序性它从不保证原子性。上面第一个例子它能治第二个例子它治不了。这篇文章讲清楚volatile到底保证了什么、不保证什么底层靠什么实现以及什么场景该用、什么场景千万别用。环境说明本文基于 JDK 8HotSpot 虚拟机x86 架构。一、先复现复现 1可见性问题——不加volatile循环停不下来publicclassVisibilityDemo{// 注意这里没有 volatileprivatestaticbooleanflagfalse;publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadworkernewThread(()-{System.out.println(worker: 开始循环);while(!flag){// 空转等待 flag 变成 true}System.out.println(worker: 检测到 flagtrue退出);});worker.start();Thread.sleep(1000);// 让 worker 先转起来flagtrue;// 主线程改标志位System.out.println(main: 已把 flag 设为 true);}}按直觉主线程 1 秒后把flag改成trueworker应该马上退出。但实际运行-server模式或生产环境下worker: 开始循环 main: 已把 flag 设为 true worker 永远不打印退出程序挂住主线程明明改了flagworker却始终看到旧值false。只要给flag加上volatileprivatestaticvolatilebooleanflagfalse;worker立刻就能退出。这说明**“一个线程的修改另一个线程不一定看得见”**这个问题真实存在而volatile能解决它。复现 2原子性缺失——加了volatilecount照样出错那volatile是不是万能的再看一个publicclassAtomicityDemo{privatestaticvolatileintcount0;// 这次加了 volatilepublicstaticvoidmain(String[]args)throwsInterruptedException{Runnabletask()-{for(inti0;i10000;i){count;// 自增 1 万次}};Threadt1newThread(task);Threadt2newThread(task);t1.start();t2.start();t1.join();t2.join();System.out.println(期望20000实际count);}}两个线程各自增 1 万次期望结果是20000。但实际跑出来期望20000实际13472每次运行结果还不一样但几乎永远小于 20000。加了volatile也没用。两个现象放一起问题就来了同样是volatile为什么它能解决第一个死循环却解决不了第二个计数丢失二、根因/底层JMM 与三大特性要讲清楚这两个现象得先认识 Java 内存模型JMM。2.1 为什么会有可见性问题现代 CPU 有多级缓存。为了性能线程操作变量时并不是直接读写主内存RAM而是先把变量拷贝一份到自己的工作内存对应 CPU 寄存器、L1/L2 缓存里之后的读写都在工作内存进行什么时候刷回主内存不确定。JMM 把这个结构抽象成主内存所有线程共享 每个线程私有的工作内存。于是复现 1 的死循环就说得通了主线程把flag true写进了自己的工作内存还没来得及或没必要刷回主内存更关键的是worker线程在while (!flag)里反复读的是它自己工作内存里的副本那份副本一直是falseJIT 编译器甚至会把while (!flag)优化成if (!flag) { while (true) {} }——因为它认为循环体里没人改flag。结果就是主线程的修改worker永远看不见。这就是可见性问题。2.2volatile的可见性volatile对可见性的保证靠两条规则写一个volatile变量时JMM 会立即把工作内存的值刷回主内存读一个volatile变量时JMM 会强制从主内存重新读取让工作内存里的旧副本作废。所以flag加上volatile后主线程一改值马上进主内存worker每次循环都从主内存读自然就读到了true。可见性问题解决。2.3 有序性与指令重排volatile保证的第二件事是有序性。为了优化性能编译器和 CPU 会在不影响单线程结果的前提下对指令重新排序instruction reordering。单线程里这没问题但多线程下重排可能让另一个线程观察到诡异的执行顺序。volatile通过在变量读写前后插入内存屏障memory barrier来禁止这种重排屏障就像一道栏杆指令不允许越过它调换顺序。这保证了对 volatile 变量的写一定发生在后续对它的读之前也就是 JMM 里的happens-before规则。这一点在单例模式的双重检查锁定DCL里至关重要——不加volatile会读到半初始化的对象。这个话题留到下一篇专门讲。2.4 为什么原子性管不到现在回到复现 2为什么volatile int count的count还是错的关键在于count不是一个原子操作它实际是三步read从主内存读取count的当前值modify把值加 1write把结果写回主内存。volatile只保证每一步的读、写是可见的但它拦不住这三步在多线程下被切开、交错执行。看这个时序线程 A 读到count 100read时间片切换线程 B 也读到count 100read线程 A 算出 101写回write线程 B 也算出 101写回write。两个线程各自增了一次本该变成 102结果只有 101——一次自增被丢了。这就是为什么最终结果总是小于 20000。volatile的可见性在这里甚至帮了倒忙的忙它保证 B 每次读的都是最新值但当 A、B 几乎同时读时那个最新值对两人来说都是同一个旧值该丢还是丢。可见性 ≠ 原子性。三、正解volatile该用在哪、不该用在哪理解了三大特性用法就清晰了volatile适合一个线程写、多个线程读的状态标志不适合任何读-改-写的复合操作。适用场景状态标志位privatevolatilebooleanshutdownfalse;publicvoidshutdown(){shutdowntrue;}// 一处写publicvoidrun(){while(!shutdown){// 多处读doWork();}}写操作只是简单赋值不依赖旧值这正是volatile的主场轻量、无锁、保证可见。不适用场景读-改-写count、i i * 2、if (x null) x new X()这类依赖旧值的操作volatile都保证不了。该用// 方案 1加锁保证复合操作原子privateintcount0;publicsynchronizedvoidinc(){count;}// 方案 2CAS 原子类推荐无锁性能更好privateAtomicIntegercountnewAtomicInteger(0);publicvoidinc(){count.incrementAndGet();}// 方案 3高并发计数LongAdder 比 AtomicLong 更快privateLongAddercountnewLongAdder();publicvoidinc(){count.increment();}一个简单的决策原则只需要改了能被看见→volatile需要读-改-写整体不可分割→synchronized/AtomicXxxCAS/LongAdder。四、常见误区与面试高频问答Qvolatile是不是轻量级的synchronized不是。两者根本不是一个维度volatile只保证可见性 有序性不保证原子性也不提供互斥synchronized既保证可见性、有序性又保证原子性和互斥同一时刻只有一个线程进临界区。volatile是无锁的、更轻但能力也更弱。Qvolatile int i; i线程安全吗不安全。i是 read-modify-write 三步复合操作volatile只保证每步的读写可见拦不住三步被交错执行所以照样丢更新。要安全得用AtomicInteger或加锁。Qvolatile能保证a 1; b 2;这两行的原子性吗不能。volatile只作用于单个变量的单次读或写无法保证多个变量、多条语句的组合原子性。跨变量的原子操作只能靠锁。Qhappens-before 和内存屏障是什么关系happens-before 是 JMM 层面的规则/承诺volatile 写 happens-before 后续的 volatile 读内存屏障是 CPU/编译器层面实现这个承诺的手段。屏障禁止重排、强制刷缓存从而让 happens-before 成立。前者是是什么后者是怎么做到。Q64 位的long/double不加volatile会有什么问题JMM 规范允许 JVM 把 64 位变量的读写拆成两次 32 位操作。极端情况下一个线程可能读到写了一半的值高 32 位是新的、低 32 位是旧的即写撕裂。加volatile后JMM 保证long/double的读写是原子的。虽然主流 64 位 JVM 实际上已经原子处理但规范上不加volatile是有隐患的。总结volatile保证线程安全是个危险的误解。它的能力边界很清楚可见性✅写立即刷主内存、读强制读主内存一个线程的修改对其他线程立即可见——解决了复现 1 的死循环。有序性✅通过内存屏障禁止指令重排保证 happens-before——这是 DCL 单例必须加volatile的原因。原子性❌只保证单次读/写可见保证不了count这种读-改-写复合操作——这是复现 2 计数丢失的根因。一句话记忆volatile 可见性 有序性不含原子性。标志位一写多读放心用count读-改-写千万别用那是AtomicInteger和锁的活。

相关新闻

华为Bootloader解锁终极指南:用PotatoNV实现免拆机自由定制

华为Bootloader解锁终极指南:用PotatoNV实现免拆机自由定制

华为Bootloader解锁终极指南:用PotatoNV实现免拆机自由定制 【免费下载链接】PotatoNV Unlock the bootloader on Huawei devices with Kirin 620/65x/95x/960 项目地址: https://gitcode.com/gh_mirrors/po/PotatoNV 你是否对华为手机的系统限制感到束手无策…

2026/7/28 17:28:39 阅读更多 →
Unity 物理实战:射线检测实现角色瞄准与射击功能

Unity 物理实战:射线检测实现角色瞄准与射击功能

Unity 物理实战:射线检测实现角色瞄准与射击功能📚 本章学习目标:深入理解射线检测实现角色瞄准与射击功能的核心概念与实践方法,掌握关键技术要点,了解实际应用场景与最佳实践。本文属于《Unity工程师成长之路教程》U…

2026/7/28 17:35:22 阅读更多 →
从0到1搭建WAF检测环境:identYwaf安装与配置完全手册

从0到1搭建WAF检测环境:identYwaf安装与配置完全手册

从0到1搭建WAF检测环境:identYwaf安装与配置完全手册 【免费下载链接】identYwaf Blind WAF identification tool 项目地址: https://gitcode.com/gh_mirrors/id/identYwaf identYwaf是一款基于盲推理技术的Web应用防火墙(WAF)识别工具…

2026/7/27 17:02:51 阅读更多 →

最新新闻

百度页面

百度页面

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>百度</title><style type"text/css">div{text-align: center;font-size: 14px;}.ngv,.bk{height: 60px;}.txt{width: 420px;height: 25px;}.bd{width…

2026/7/28 18:30:12 阅读更多 →
AI水印不是加个logo!揭秘频域嵌入、神经指纹与语义水印三大范式性能对比(含FID/PSNR/Breaking Rate实测数据)

AI水印不是加个logo!揭秘频域嵌入、神经指纹与语义水印三大范式性能对比(含FID/PSNR/Breaking Rate实测数据)

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;AI水印技术演进与核心挑战 AI水印技术正从早期的图像域嵌入&#xff0c;逐步迈向跨模态、鲁棒性驱动与语义对齐的新阶段。早期方法如LSB替换或DCT域量化水印虽实现简单&#xff0c;但面对模型微调、重采样或提…

2026/7/28 18:30:12 阅读更多 →
springboot+JWT+Shiro教程整理

springboot+JWT+Shiro教程整理

Shiro JWT Spring Boot Restful 简易教程https://github.com/Smith-Cruise/Spring-Boot-Shiro?spma2c4e.10696291.0.0.683e19a4WCruMW SSM整合shiro实现多用户表多Realm统一登录认证https://blog.csdn.net/visket2008/article/details/78539334

2026/7/28 18:30:12 阅读更多 →
计算机网络 - 练习(二十四)

计算机网络 - 练习(二十四)

计算机网络 练习&#xff08;二十四&#xff09; 在面向对象的软件工程中&#xff0c;一个组件&#xff08;component&#xff09;包含了&#xff08;&#xff09;。 A. 所有的属性和操作 B. 各个类的实例 C. 每个设备或用户 (device or user) 的作用 D. 一些协作的类的集合 -…

2026/7/28 18:30:12 阅读更多 →
生成式AI备案实操白皮书:从材料准备到技术评估的11个隐藏否决项(含3家过审企业原始文档脱敏版)

生成式AI备案实操白皮书:从材料准备到技术评估的11个隐藏否决项(含3家过审企业原始文档脱敏版)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;AI合规使用指南 在企业与个人广泛采用生成式AI工具的当下&#xff0c;确保AI使用行为符合法律法规、行业规范及组织内部政策&#xff0c;已成为技术实践的前置要件。合规并非限制创新&#xff0c;而是为可持续…

2026/7/28 18:30:12 阅读更多 →
全网最简单的电脑店记账方法|用电脑掌柜ERP,0基础3分钟理清每一笔账 2026 + 软件分享

全网最简单的电脑店记账方法|用电脑掌柜ERP,0基础3分钟理清每一笔账 2026 + 软件分享

作为一名在电脑行业摸爬滚打了 15 年 的老店主&#xff0c;我见过太多同行把生意做黄&#xff0c;不是因为技术不行&#xff0c;而是因为账算不清楚。一台整机赚 200 还是亏 50&#xff0c;月底一算全是"大概""好像""应该"。今天这篇文章&#x…

2026/7/28 18:29:12 阅读更多 →

日新闻

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿&#xff01;3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:43 阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑&#xff1a;把几百页的财报、法规、技术手册扔给向量库&#xff0c;问一个具体问题&#xff0c;搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了&#xff0c;要么藏在几十条结果的最下面。语义相似≠真正相关&#xff0c;这个…

2026/7/28 0:00:43 阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营&#xff0c;从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候&#xff0c;每天刷半小时抖音&#xff0c;手动把爆款视频的口播敲进备忘录&#xff0c;一条2分钟的视频得花十来分钟&#xff0c;碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:43 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档&#xff0c;可以直接使用&#xff01;系统支持图片、视频、摄像头等多种方式检测裂缝&#xff0c;功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像&#xff01; pubg绝地求生目标检测数据集 1分类&#xff1a;e_body&#xff0c;14905个标签&#xff0c;txt格式 共计14244张图&#xff0c;99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别&#xff1a; allies enemy tag图片总量&#xff1a;7247张训练集&#xff1a;5139张验证集&#xff1a;1425张测试集&#xff1a;683张标注状态&#xff1a;全部已标注&#xff0c;即拿即用数据格式&#xff1a;支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻