volatile关键字用于解决多线程变量可见性问题,每次读取从主内存加载、写入立即刷新回主内存,并禁止指令重排序;适用于状态标志、DCL单例等简单读写场景,但不保证原子性和互斥。

volatile 关键字在 Java 中主要用于解决多线程环境下变量的可见性问题,它不保证原子性,也不提供互斥锁功能,但能确保一个线程对 volatile 变量的修改立即对其他线程可见。
volatile 如何保证可见性
Java 内存模型(JMM)规定,每个线程有自己的工作内存(如 CPU 缓存),线程读写变量时操作的是本地副本。普通变量可能被编译器优化或缓存长期不刷新,导致其他线程看不到最新值。而声明为 volatile 的变量:
- 每次读取都从主内存直接加载最新值,不使用缓存副本
- 每次写入都立即刷新回主内存,不等待写缓冲区合并
- 禁止指令重排序:编译器和处理器不会将 volatile 读写操作与其他内存操作随意重排(插入内存屏障)
典型适用场景
volatile 适合用在状态标志、单次发布的对象引用等不需要原子性保障的简单读写场景:
- 线程间通信的开关标志(如
volatile boolean running = true;) - 双重检查锁定(DCL)中初始化单例对象的实例字段(
private static volatile Singleton instance;) - 仅由一个线程写、多个线程读的共享变量(如配置参数更新后广播)
注意:如果涉及复合操作(如 count++),volatile 无法保证原子性,必须配合 synchronized 或 AtomicInteger。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
常见误区与限制
volatile 不是万能的同步工具,容易误用:
- 不能替代 synchronized:它不阻止多个线程同时进入临界区,不提供互斥
-
不保证复合操作的原子性:例如
flag = !flag或counter++仍存在竞态条件 - 对 long/double 类型的读写不是原子的? 在 32 位 JVM 上,非 volatile 的 long/double 可能出现“半个值”问题;volatile 能保证其读写原子性(JVM 规范要求)
简单示例:停止线程的正确方式
以下代码演示如何用 volatile 安全地通知线程退出:
public class VolatileExample {
private volatile boolean stopRequested = false;
public void start() {
new Thread(() -> {
while (!stopRequested) {
// 执行任务...
try { Thread.sleep(100); } catch (InterruptedException e) { break; }
}
System.out.println("线程已停止");
}).start();
}
public void requestStop() {
stopRequested = true; // 立即对工作线程可见
}
}
若去掉 volatile,主线程设为 true 后,工作线程可能永远读不到新值,陷入死循环。

















