0

0

volatile关键字有什么作用_并发可见性问题解析

P粉602998670

P粉602998670

发布时间:2026-01-12 17:51:16

|

494人浏览过

|

来源于php中文网

原创

volatile解决线程间变量不可见和指令重排序问题,保证可见性与禁止重排序,但不保证原子性;如volatile int count的count++仍非原子操作,需用AtomicInteger等替代。

volatile关键字有什么作用_并发可见性问题解析

volatile 解决什么问题:线程改了值,另一个线程却“看不见”

核心作用就两条:保证可见性禁止指令重排序。它不解决原子性——这点必须先划重点。比如 volatile int countcount++ 仍然是非原子操作,会出错。

典型现象是“死循环卡住”:一个线程把 flag = true,另一个线程还在 while(!flag) 里转圈,永远不退出。这不是代码逻辑错,而是 JVM 和 CPU 的缓存优化导致的——线程各自读写自己的 CPU 缓存,没及时同步到主内存。

  • 普通变量:修改只写入本线程工作内存(CPU 缓存),刷新主内存时机不确定
  • volatile 变量:写操作强制刷回主内存;读操作强制从主内存加载最新值
  • 底层靠 MESI 缓存一致性协议 + 总线嗅探实现,不是靠锁,所以轻量

什么时候必须用 volatile:状态标志、双重检查单例

适用场景非常明确,不是所有共享变量都该加 volatile。滥用反而掩盖真正需要同步的地方。

  • 状态标志位:如 volatile boolean isRunningvolatile boolean shutdownRequested,只做开关控制,无复合操作
  • 双重检查单例(DCL)中的 instance 引用:防止对象构造未完成就被其他线程看到(避免“半初始化”对象)
  • 作为 happens-before 的锚点:配合其他操作建立内存可见性顺序,比如在写 volatile 前写普通变量,能保证该普通变量对后续读该 volatile 的线程可见

反例:不要用它保护计数器、累加器、集合操作等——这些必须用 AtomicIntegersynchronized

为什么 volatile++ 不行:原子性缺失的真实代价

volatile 保证的是“单次读或单次写”的原子性(如 booleanint 赋值),但 count++ 是三步:read → modify → write,中间可能被其他线程打断。

知了追踪
知了追踪

AI智能信息助手,智能追踪你的兴趣资讯

下载
int count = 0; // 非 volatile
// 线程 A 执行 count++
// 1. 读 count = 0
// 2. 计算 0 + 1 = 1
// 3. 写回 count = 1

// 线程 B 同时执行 count++
// 1. 读 count = 0(此时 A 还没写回)
// 2. 计算 0 + 1 = 1
// 3. 写回 count = 1 → 覆盖 A 的结果

// 最终 count = 1,而非预期的 2
  • 即使加了 volatile,上面三步仍可被交叉执行
  • 想安全累加,用 AtomicInteger.incrementAndGet()
  • 想保护一段逻辑,用 synchronizedReentrantLock

容易忽略的坑:没有 happens-before,volatile 也救不了你

volatile 的可见性不是“全局广播”,它只对**该变量本身**生效。如果还有其他非 volatile 变量依赖它的状态,顺序没约束,照样可能出错。

例如:

private volatile boolean ready = false;
private int data = 0;

// 线程 A
data = 42;           // 普通写
ready = true;        // volatile 写 → 触发 happens-before 关系

// 线程 B
if (ready) {         // volatile 读
    System.out.println(data); // 此处能安全看到 data == 42
}
  • 关键在 ready = truedata = 42 的先后顺序——JVM 保证前者 happens-before 后者,才让线程 B 读到 data 的新值
  • 但如果线程 A 先写 ready = true,再写 data = 42,线程 B 就可能读到 ready == truedata == 0
  • 所以 volatile 必须和正确的执行顺序配合,不能只靠“加了关键字就万事大吉”

最常被低估的,是它只管“这个变量”,不管“这个变量周围的代码”。一旦涉及多个变量协作或复杂状态流转,就得上更重的同步机制

相关专题

更多
java中boolean的用法
java中boolean的用法

在Java中,boolean是一种基本数据类型,它只有两个可能的值:true和false。boolean类型经常用于条件测试,比如进行比较或者检查某个条件是否满足。想了解更多java中boolean的相关内容,可以阅读本专题下面的文章。

348

2023.11.13

java boolean类型
java boolean类型

本专题整合了java中boolean类型相关教程,阅读专题下面的文章了解更多详细内容。

23

2025.11.30

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

196

2023.11.20

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

84

2023.09.25

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

534

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

52

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

194

2025.08.29

Java 项目构建与依赖管理(Maven / Gradle)
Java 项目构建与依赖管理(Maven / Gradle)

本专题系统讲解 Java 项目构建与依赖管理的完整体系,重点覆盖 Maven 与 Gradle 的核心概念、项目生命周期、依赖冲突解决、多模块项目管理、构建加速与版本发布规范。通过真实项目结构示例,帮助学习者掌握 从零搭建、维护到发布 Java 工程的标准化流程,提升在实际团队开发中的工程能力与协作效率。

10

2026.01.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 45.1万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号