好的,我来帮你整理一份 Java 中 volatile
关键字详解,从概念、原理、使用场景到注意事项,深入讲解并配示例代码。
🔹 1. volatile
简介
- 类型:Java 关键字
- 作用:确保 多线程环境下的可见性 和 防止指令重排
- 主要用于 轻量级同步,替代部分
synchronized
的场景
🔹 2. volatile
的核心特性
- 可见性(Visibility)
- 一个线程修改了
volatile
变量,立即对其他线程可见 - 普通变量在多线程下可能会被 CPU 缓存,其他线程不一定能立即看到
- 一个线程修改了
- 禁止指令重排序(Ordering)
- 编译器和 CPU 在优化时可能会改变指令执行顺序
volatile
会在 读/写操作前后加入内存屏障(Memory Barrier),保证顺序
- 不保证原子性(Atomicity)
- 自增
count++
等操作不是原子操作 - 对单次写入(赋值)是原子的,但复合操作需要额外同步
- 自增
🔹 3. 使用场景
- 状态标志(Flag)
class Task implements Runnable {
private volatile boolean running = true;
public void stop() {
running = false;
}
@Override
public void run() {
while (running) {
// 执行任务
}
System.out.println("线程已停止");
}
}
- 一个线程修改
running
,其他线程可以立即感知
- 单例模式(Double-Check Locking)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile
防止 对象未完全初始化就被其他线程访问
🔹 4. 内存模型(JMM)相关
volatile
与 Java 内存模型(JMM) 密切相关- 保证:
- 写操作先于后续读操作
- 写入的值对所有线程立即可见
- CPU 缓存和寄存器的优化不会影响
volatile
变量
🔹 5. 代码示例:可见性演示
class VolatileDemo {
private static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (flag) {
// 循环等待 flag 变为 false
}
System.out.println("线程结束");
}).start();
Thread.sleep(1000);
flag = false; // 主线程修改 flag
System.out.println("主线程修改 flag");
}
}
- 如果去掉
volatile
,子线程可能 永远循环 - 加上
volatile
,子线程能及时感知变化
🔹 6. 注意事项
- 不能保证复合操作原子性
volatile int count = 0;
count++; // 不安全,可能丢失更新
- 复合操作需用
synchronized
或AtomicInteger
- 适用场景有限
- 适合 状态标志、读多写少的场景
- 不适合 计数器、累加器等高并发写操作
- 与 synchronized 对比
| 特性 | volatile | synchronized |
|—————–|—————–|——————|
| 可见性 | ✔ | ✔ |
| 原子性 | ✘ | ✔ |
| 性能 | 高 | 相对低 |
| 适用场景 | 状态标志、单例 | 复合操作、方法同步 |
✅ 总结
volatile
是 轻量级同步工具,主要解决 可见性和顺序问题- 单次写入原子,但复合操作不保证原子性
- 常用于 标志位、DCL单例模式 等场景
- 高并发计数器等需要
AtomicXXX
或synchronized
发表回复