跳到主要内容

volatile关键字完全指南

学习目标:掌握volatile的核心概念、使用场景、面试要点,能够正确在实际项目中使用volatile

📚 学习导航


为什么需要volatile?🤔

一个真实的问题场景

想象一下这个场景:你在看电视,妈妈在厨房做饭。妈妈告诉你"饭好了叫我",但你因为太专注看电视而没有听到。这就是可见性问题

public class TVWatchingExample {
// 妈妈设置的标记:饭好了
private boolean dinnerReady = false; // 没有volatile!

public void momCooksDinner() {
System.out.println("妈妈开始做饭...");
try {
Thread.sleep(2000); // 做饭需要2秒
} catch (InterruptedException e) {
e.printStackTrace();
}

dinnerReady = true; // 妈妈设置:饭好了!
System.out.println("妈妈:饭好了,快来吃饭!");
}

public void kidWatchesTV() {
System.out.println("孩子开始看电视...");

// 一直看电视,不知道饭已经好了
while (!dinnerReady) {
// 永远循环下去,孩子永远不知道饭好了!
}

System.out.println("孩子:来了来了,吃饭!");
}

public static void main(String[] args) {
TVWatchingExample example = new TVWatchingExample();

// 妈妈在厨房做饭(一个线程)
new Thread(example::momCooksDinner).start();

// 孩子在客厅看电视(另一个线程)
new Thread(example::kidWatchesTV).start();
}
}

运行结果:孩子永远看电视,不知道饭已经好了!😱

问题在哪里?

每个线程都有自己的"小内存"(工作内存),dinnerReady变量的更新可能只停留在妈妈线程的小内存中,孩子线程看不到这个更新。

线程内存模型图

解决方案:加上volatile

private volatile boolean dinnerReady = false;  // 加上volatile!

运行结果:孩子能够及时知道饭好了,去吃饭!😊

这就是volatile最核心的作用:保证变量在多线程之间的可见性


volatile的三个核心作用 🎯

1. 可见性(Visibility)- 让其他线程看到你的修改

什么是可见性?

当一个线程修改了volatile变量,这个修改会立即被其他线程看到。

public class VolatileVisibilityDemo {
// 对比有无volatile的区别
private boolean withoutVolatile = false;
private volatile boolean withVolatile = false;

public void demonstrateWithoutVolatile() {
System.out.println("=== 没有volatile的情况 ===");

// 写线程
new Thread(() -> {
try {
Thread.sleep(1000);
withoutVolatile = true;
System.out.println("写线程:withoutVolatile已设置为true");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();

// 读线程
new Thread(() -> {
int count = 0;
while (!withoutVolatile) {
count++;
if (count % 100000000 == 0) {
System.out.println("读线程:还在等待... (已循环" + (count/100000000) + "亿次)");
}
}
System.out.println("读线程:终于看到了withoutVolatile的变化!");
}).start();
}

public void demonstrateWithVolatile() {
System.out.println("\n=== 有volatile的情况 ===");

// 写线程
new Thread(() -> {
try {
Thread.sleep(1000);
withVolatile = true;
System.out.println("写线程:withVolatile已设置为true");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();

// 读线程
new Thread(() -> {
int count = 0;
while (!withVolatile) {
count++;
}
System.out.println("读线程:很快就看到了withVolatile的变化!(只循环了" + count + "次)");
}).start();
}

public static void main(String[] args) {
VolatileVisibilityDemo demo = new VolatileVisibilityDemo();

// 先演示没有volatile的情况
demo.demonstrateWithoutVolatile();

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

// 再演示有volatile的情况
demo.demonstrateWithVolatile();
}
}

2. 有序性(Ordering)- 防止指令重排序

什么是指令重排序?

编译器和CPU为了优化性能,可能会改变代码的执行顺序。

public class ReorderingExample {
private int configValue = 0;
private volatile boolean initialized = false;

public void initialize() {
// 步骤1:设置配置值
configValue = 42;

// 步骤2:标记初始化完成
initialized = true; // volatile写,防止重排序

// 如果没有volatile,这两步可能被重排序:
// 1. 先设置 initialized = true
// 2. 再设置 configValue = 42
// 这样其他线程可能看到 initialized=true 但 configValue=0
}

public void useConfig() {
if (initialized) { // volatile读
// 因为有volatile保证,这里一定能看到 configValue = 42
System.out.println("配置值: " + configValue);
}
}
}

重排序的危害:著名的双重检查锁定(DCL)问题

public class Singleton {
// 如果没有volatile,会出现问题!
private static volatile Singleton instance; // 必须加volatile

private Singleton() {
// 构造函数可能很复杂
}

public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton(); // 可能被重排序!
}
}
}
return instance;
}
}

为什么需要volatile?

instance = new Singleton() 实际包含三个步骤:

  1. 分配内存空间
  2. 初始化对象
  3. 将instance指向内存地址

如果没有volatile,可能被重排序为 1→3→2,导致其他线程拿到未初始化的对象!

3. 部分原子性(Partial Atomicity)- 仅保证单次读写的原子性

重要:volatile不保证复合操作的原子性!

public class AtomicityDemo {
private volatile int counter = 0;

// ❌ 这不是原子操作!
public void badIncrement() {
counter++; // 实际包含三个步骤:
// 1. 读取counter的值
// 2. 加1
// 3. 写入新值
}

// ✅ 这样是原子的
public void atomicSet(int newValue) {
counter = newValue; // 单次写入,是原子的
}

// ✅ 使用原子类保证复合操作的原子性
private final AtomicInteger atomicCounter = new AtomicInteger(0);

public void goodIncrement() {
atomicCounter.incrementAndGet(); // 原子的递增操作
}
}

总结volatile的三个特性:

特性说明例子
可见性修改对其他线程立即可见状态标记、配置更新
有序性禁止指令重排序双重检查锁定
部分原子性仅保证单次读写的原子性x = 5 是原子的,x++ 不是

什么时候应该使用volatile?🎯

适用场景1:状态标记位

最经典的使用场景:控制线程的生命周期

public class ServerApplication {
// 控制服务器是否运行的状态标记
private volatile boolean running = true;

public void startServer() {
System.out.println("服务器启动中...");

// 启动工作线程
new Thread(this::worker).start();

// 启动监控线程
new Thread(this::monitor).start();

System.out.println("服务器已启动");
}

private void worker() {
while (running) {
try {
// 模拟工作处理
System.out.println("处理客户端请求...");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
System.out.println("工作线程停止");
}

private void monitor() {
while (running) {
try {
// 模拟监控任务
System.out.println("检查系统状态...");
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
System.out.println("监控线程停止");
}

// 优雅停止服务器
public void stopServer() {
System.out.println("正在停止服务器...");
running = false; // 所有线程都会看到这个变化
}

public static void main(String[] args) throws InterruptedException {
ServerApplication server = new ServerApplication();
server.startServer();

// 运行5秒后停止
Thread.sleep(5000);
server.stopServer();
}
}

适用场景2:双重检查锁定(DCL)

最经典的单例模式实现

public class DatabaseConnectionPool {
// 必须使用volatile防止重排序
private static volatile DatabaseConnectionPool instance;

private final Connection[] connections;
private final int maxSize;

private DatabaseConnectionPool() {
this.maxSize = 10;
this.connections = new Connection[maxSize];
System.out.println("数据库连接池初始化完成");
// 模拟复杂的初始化过程
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

public static DatabaseConnectionPool getInstance() {
// 第一次检查:避免不必要的同步
if (instance == null) {
synchronized (DatabaseConnectionPool.class) {
// 第二次检查:防止多线程创建多个实例
if (instance == null) {
instance = new DatabaseConnectionPool();
}
}
}
return instance;
}

public Connection getConnection() {
// 简化的连接获取逻辑
for (int i = 0; i < connections.length; i++) {
if (connections[i] == null || connections[i].isClosed()) {
connections[i] = createNewConnection();
return connections[i];
}
}
throw new RuntimeException("连接池已满");
}

private Connection createNewConnection() {
// 创建数据库连接
return new Connection() {
private boolean closed = false;

@Override
public boolean isClosed() {
return closed;
}

// 省略其他方法...
};
}
}

适用场景3:配置信息发布

一次写入,多次读取的场景

public class ApplicationConfig {
// 配置信息不经常变化,但需要立即可见
private volatile String environment;
private volatile boolean debugMode;
private volatile int maxConnections;
private volatile long sessionTimeout;

// 使用不可变对象封装配置
private volatile Config currentConfig;

public ApplicationConfig() {
this.currentConfig = new Config("development", true, 10, 1800000);
updateFromConfig();
}

// 配置更新(写操作较少)
public void updateConfig(String env, boolean debug, int maxConn, long timeout) {
Config newConfig = new Config(env, debug, maxConn, timeout);
this.currentConfig = newConfig; // 一次volatile写入
updateFromConfig();
}

private void updateFromConfig() {
Config config = this.currentConfig;
this.environment = config.environment;
this.debugMode = config.debugMode;
this.maxConnections = config.maxConnections;
this.sessionTimeout = config.sessionTimeout;
}

// 配置读取(读操作频繁)
public String getEnvironment() {
return environment;
}

public boolean isDebugMode() {
return debugMode;
}

public int getMaxConnections() {
return maxConnections;
}

public long getSessionTimeout() {
return sessionTimeout;
}

// 不可变的配置类
private static class Config {
final String environment;
final boolean debugMode;
final int maxConnections;
final long sessionTimeout;

Config(String environment, boolean debugMode, int maxConnections, long sessionTimeout) {
this.environment = environment;
this.debugMode = debugMode;
this.maxConnections = maxConnections;
this.sessionTimeout = sessionTimeout;
}
}
}

适用场景4:观察者模式的状态发布

线程安全的状态通知机制

public class TaskScheduler {
// 任务调度状态
private volatile Task currentTask;
private volatile boolean schedulerRunning = false;
private volatile long lastExecutionTime = 0;

// 工作线程池
private final ExecutorService workerPool;

public TaskScheduler(int workerCount) {
this.workerPool = Executors.newFixedThreadPool(workerCount);
}

// 提交新任务
public void submitTask(Task task) {
this.currentTask = task; // 所有工作线程立即可见
System.out.println("任务已提交: " + task.getName());
}

// 启动调度器
public void start() {
if (!schedulerRunning) {
schedulerRunning = true;
workerPool.submit(this::schedulerLoop);
System.out.println("任务调度器已启动");
}
}

// 停止调度器
public void stop() {
schedulerRunning = false;
workerPool.shutdown();
System.out.println("任务调度器已停止");
}

private void schedulerLoop() {
while (schedulerRunning) {
Task task = currentTask;
if (task != null && !task.isCompleted()) {
try {
System.out.println("执行任务: " + task.getName());
task.execute();
lastExecutionTime = System.currentTimeMillis();
System.out.println("任务完成: " + task.getName());
} catch (Exception e) {
System.err.println("任务执行失败: " + e.getMessage());
}
}

try {
Thread.sleep(1000); // 每秒检查一次
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}

public static class Task {
private final String name;
private volatile boolean completed = false;

public Task(String name) {
this.name = name;
}

public void execute() throws InterruptedException {
// 模拟任务执行
Thread.sleep(500);
completed = true;
}

public boolean isCompleted() {
return completed;
}

public String getName() {
return name;
}
}
}

常见的使用误区 ⚠️

误区1:认为volatile保证原子性

最常见的错误:用volatile做计数器

public class VolatileCounter {
// ❌ 错误!volatile不保证i++的原子性
private volatile int counter = 0;

public void increment() {
counter++; // 这不是原子操作!
// 等价于:
// 1. 读取counter的值
// 2. 加1
// 3. 写回新值
}

public int getValue() {
return counter;
}

public static void main(String[] args) throws InterruptedException {
VolatileCounter counter = new VolatileCounter();

// 创建10个线程,每个线程递增1000次
int threadCount = 10;
int incrementsPerThread = 1000;

for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < incrementsPerThread; j++) {
counter.increment();
}
}).start();
}

Thread.sleep(2000); // 等待所有线程完成

System.out.println("期望结果: " + (threadCount * incrementsPerThread));
System.out.println("实际结果: " + counter.getValue());
System.out.println("是否正确: " + (counter.getValue() == threadCount * incrementsPerThread));
}
}

正确的解决方案

public class CorrectCounter {
// ✅ 方案1:使用原子类
private final AtomicInteger atomicCounter = new AtomicInteger(0);

// ✅ 方案2:使用synchronized
private int synchronizedCounter = 0;
private final Object lock = new Object();

// ✅ 方案3:使用LongAdder(高并发场景)
private final LongAdder adderCounter = new LongAdder();

// 原子类方式
public void atomicIncrement() {
atomicCounter.incrementAndGet();
}

// 同步方式
public void synchronizedIncrement() {
synchronized(lock) {
synchronizedCounter++;
}
}

// LongAdder方式
public void adderIncrement() {
adderCounter.increment();
}
}

误区2:过度使用volatile

问题代码:每个变量都用volatile

public class OveruseVolatile {
// ❌ 过度使用!不必要的volatile声明
private volatile int id;
private volatile String name;
private volatile int age;
private volatile boolean active;
private volatile double salary;

// ✅ 正确的方式:只在必要时使用volatile
private volatile boolean configChanged = false;

// 其他变量使用同步保护
private int id;
private String name;
private int age;
private boolean active;
private double salary;

// 批量更新,减少同步开销
public synchronized void updateEmployee(int id, String name, int age, boolean active, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.active = active;
this.salary = salary;
this.configChanged = true; // 只在最后设置volatile标记
}

public boolean hasConfigChanged() {
return configChanged;
}

public synchronized void resetConfigChanged() {
configChanged = false;
}
}

误区3:check-then-act操作

经典的银行转账问题

public class BankAccount {
private volatile double balance;

public BankAccount(double initialBalance) {
this.balance = initialBalance;
}

// ❌ 危险!不是原子操作
public boolean withdraw(double amount) {
if (balance >= amount) { // 读取余额
// 在这里可能被其他线程修改余额
balance -= amount; // 扣除余额
return true;
}
return false;
}

// ✅ 正确的方式:使用synchronized
public synchronized boolean safeWithdraw(double amount) {
if (balance >= amount) {
balance -= amount;
return true;
}
return false;
}

// ✅ 或者使用CAS操作
private final AtomicReference<Double> atomicBalance = new AtomicReference<>();

public boolean casWithdraw(double amount) {
Double currentBalance;
Double newBalance;
do {
currentBalance = atomicBalance.get();
if (currentBalance < amount) {
return false;
}
newBalance = currentBalance - amount;
} while (!atomicBalance.compareAndSet(currentBalance, newBalance));
return true;
}
}

volatile vs synchronized 对比 🆚

详细对比表

特性volatilesynchronized
可见性✅ 保证✅ 保证
原子性❌ 仅单次读写✅ 完整保证
有序性✅ 禁止重排序✅ 完全保证
性能开销🟢 较低🟡 较高
阻塞机制❌ 不会阻塞✅ 可能阻塞
使用范围变量级别方法/代码块
适用场景简单状态标记复合操作
内存屏障轻量级重量级

性能对比测试

public class PerformanceComparison {
private volatile int volatileCounter = 0;
private int synchronizedCounter = 0;
private final Object lock = new Object();

// volatile读取测试
public void volatileRead() {
int value = volatileCounter; // 无锁读取
}

// volatile写入测试
public void volatileWrite() {
volatileCounter = 1; // 单次写入
}

// synchronized读取测试
public void synchronizedRead() {
synchronized(lock) {
int value = synchronizedCounter; // 有锁读取
}
}

// synchronized写入测试
public void synchronizedWrite() {
synchronized(lock) {
synchronizedCounter = 1; // 有锁写入
}
}

// 性能基准测试
public static void main(String[] args) throws InterruptedException {
PerformanceComparison test = new PerformanceComparison();
int iterations = 1000000;
int threadCount = 4;

// 测试volatile读取性能
long start = System.nanoTime();
Thread[] volatileReaders = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
volatileReaders[i] = new Thread(() -> {
for (int j = 0; j < iterations; j++) {
test.volatileRead();
}
});
volatileReaders[i].start();
}
for (Thread t : volatileReaders) {
t.join();
}
long volatileReadTime = System.nanoTime() - start;

// 测试synchronized读取性能
start = System.nanoTime();
Thread[] syncReaders = new Thread[threadCount];
for (int i = 0; i < threadCount; i++) {
syncReaders[i] = new Thread(() -> {
for (int j = 0; j < iterations; j++) {
test.synchronizedRead();
}
});
syncReaders[i].start();
}
for (Thread t : syncReaders) {
t.join();
}
long syncReadTime = System.nanoTime() - start;

System.out.println("=== 性能测试结果 ===");
System.out.println("Volatile 读取时间: " + (volatileReadTime / 1_000_000) + " ms");
System.out.println("Synchronized 读取时间: " + (syncReadTime / 1_000_000) + " ms");
System.out.println("性能比: " + String.format("%.2f", (double) syncReadTime / volatileReadTime));

if (syncReadTime > volatileReadTime) {
System.out.println("✅ volatile读取更快,适合读多写少场景");
}
}
}

面试必考题 🔥

问题1:volatile的作用是什么?

标准答案模板

volatile主要有三个作用:
1. 可见性:当一个线程修改了volatile变量,其他线程能够立即看到这个修改
2. 有序性:禁止指令重排序,确保volatile操作前后的指令执行顺序
3. 部分原子性:保证volatile变量的单次读写操作是原子的,但不保证复合操作的原子性

但是volatile不能替代synchronized,因为它不保证复合操作的原子性。

问题2:volatile和synchronized的区别?

回答框架

可以从以下几个维度对比:

1. 原子性:
- volatile:只保证单次读写的原子性
- synchronized:保证整个代码块的原子性

2. 可见性:
- 两者都保证可见性

3. 有序性:
- volatile:禁止指令重排序
- synchronized:保证代码块内外的有序性

4. 性能:
- volatile:轻量级,性能较好
- synchronized:重量级,性能开销较大

5. 阻塞:
- volatile:不会阻塞线程
- synchronized:会阻塞线程

6. 使用场景:
- volatile:适合状态标记、配置更新等简单场景
- synchronized:适合复合操作、需要原子性保证的场景

问题3:双重检查锁定为什么要用volatile?

关键代码和解释

public class Singleton {
// 必须使用volatile
private static volatile Singleton instance;

public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton(); // 可能被重排序!
}
}
}
return instance;
}
}

解释要点

instance = new Singleton() 在JVM中分为三步:
1. 分配内存空间
2. 初始化对象
3. 将instance指向内存地址

如果没有volatile,可能发生重排序:1→3→2
这样其他线程可能在步骤3之后看到instance非null,但对象还未初始化
使用volatile可以防止这种重排序,确保其他线程看到的是完整初始化的对象

问题4:volatile的实现原理是什么?

技术要点

volatile通过内存屏障(Memory Barrier)实现:

1. volatile写操作:
- 在写之前插入StoreStore屏障:禁止前面的普通写与volatile写重排序
- 在写之后插入StoreLoad屏障:禁止volatile写与后面的读写重排序

2. volatile读操作:
- 在读之后插入LoadLoad屏障:禁止volatile读与后面的普通读重排序
- 在读之后插入LoadStore屏障:禁止volatile读与后面的普通写重排序

3. 硬件层面:
- 通过MESI缓存一致性协议保证缓存一致性
- 在x86架构下,StoreLoad屏障通过lock指令实现

问题5:什么场景下应该使用volatile?

回答框架

适合使用volatile的场景:

1. 状态标记:
private volatile boolean running = true;
用于控制线程的生命周期

2. 双重检查锁定:
private static volatile Singleton instance;
防止指令重排序

3. 配置信息发布:
private volatile Config config;
一次写入,多次读取

4. 独立观察:
private volatile User currentUser;
不依赖旧值的简单赋值

不适合使用volatile的场景:

1. 复合操作:如count++
2. 依赖旧值的操作:如if(count > 0) count--
3. 需要原子性保证的复杂操作

问题6:volatile能保证线程安全吗?

深度分析

volatile不能完全保证线程安全,只能保证:
1. 变量的可见性
2. 单次读写的原子性
3. 禁止指令重排序

但是不保证复合操作的线程安全。

线程安全的定义:
1. 原子性:操作要么全部完成,要么都不完成
2. 可见性:一个线程的修改对其他线程可见
3. 有序性:程序执行的顺序与代码顺序一致

volatile只满足可见性和部分原子性,有序性也只是部分满足。

要实现完整的线程安全,需要:
- volatile + synchronized
- volatile + 原子类
- 或者直接使用synchronized

最佳实践总结 🏆

使用volatile的黄金法则

public class VolatileBestPractices {

// ✅ 法则1:只用于简单的状态标记
private volatile boolean shutdownRequested = false;
private volatile boolean paused = false;

// ✅ 法则2:用于独立观察(不依赖旧值)
private volatile Config currentConfig;

// ❌ 错误:不要用于复合操作
private volatile int counter = 0;
// counter++ 不是原子的!

// ✅ 正确:使用原子类处理复合操作
private final AtomicInteger atomicCounter = new AtomicInteger(0);

// ✅ 法则3:用于双重检查锁定
private static volatile Singleton instance;

// ✅ 法则4:一次性安全发布
private volatile Map<String, String> cacheMap;

public void initializeCache() {
Map<String, String> temp = new HashMap<>();
temp.put("key1", "value1");
temp.put("key2", "value2");
this.cacheMap = temp; // 一次写入,安全发布
}

// ✅ 最佳实践:组合使用volatile和同步
private volatile boolean dirty = false;
private int value1, value2, value3;

public void updateValues(int v1, int v2, int v3) {
synchronized(this) {
this.value1 = v1;
this.value2 = v2;
this.value3 = v3;
this.dirty = true; // 最后设置volatile标记
}
}

public int[] getValuesIfDirty() {
if (dirty) {
synchronized(this) {
if (dirty) { // 双重检查
dirty = false;
return new int[]{value1, value2, value3};
}
}
}
return null;
}
}

性能优化技巧

public class VolatileOptimization {

// 技巧1:减少volatile写入次数
private volatile int x, y, z;

public void badUpdate(int newX, int newY, int newZ) {
x = newX; // 3次volatile写入
y = newY;
z = newZ;
}

public void goodUpdate(int newX, int newY, int newZ) {
synchronized(this) { // 一次同步块
x = newX;
y = newY;
z = newZ;
}
}

// 技巧2:缓存volatile读取值
private volatile int threshold = 100;

public void processData(List<Integer> data) {
int localThreshold = threshold; // 只读取一次
for (Integer item : data) {
if (item > localThreshold) {
processItem(item);
}
}
}

// 技巧3:避免伪共享(缓存行填充)
public static class Counter {
// 添加填充,避免多个Counter对象在同一缓存行
private volatile long p1, p2, p3, p4, p5, p6, p7;
private volatile long value;
private volatile long p8, p9, p10, p11, p12, p13, p14;

public void increment() {
value++; // 注意:这不是原子的,仅示例填充
}
}

private void processItem(int item) {
// 处理逻辑
}
}

总结 💡

核心知识点回顾

  1. volatile的本质:轻量级的同步机制,主要通过内存屏障实现
  2. 三个核心特性:可见性、有序性、部分原子性
  3. 适用场景:状态标记、双重检查锁定、配置发布、独立观察
  4. 主要限制:不保证复合操作的原子性
  5. 性能特点:比synchronized轻量,适合读多写少场景

学习路线图

初学者 → 理解可见性问题 → 掌握volatile基础使用

进阶者 → 理解内存屏障 → 掌握DCL模式实现

高级者 → 理解happens-before → 掌握性能优化技巧

专家 → 理解底层实现 → 能够设计高性能并发系统

选择建议

// 场景选择决策树
public static void main(String[] args) {
// 1. 只是简单的状态标记?
// → 使用 volatile

// 2. 需要复合操作的原子性?
// → 使用 Atomic* 类或 synchronized

// 3. 读多写少的配置信息?
// → 使用 volatile + 不可变对象

// 4. 复杂的业务逻辑操作?
// → 使用 synchronized 或 ReentrantLock

// 5. 高并发的计数器场景?
// → 使用 LongAdder
}

记住:volatile是并发编程的工具之一,不是银弹。选择合适的并发工具需要根据具体场景、性能要求和复杂度来决定。


💪 实践建议:看完本文后,建议:

  1. 动手运行所有代码示例,理解volatile的实际效果
  2. 尝试修改代码,去掉volatile,观察不同的运行结果
  3. 在实际项目中寻找合适的volatile使用场景
  4. 结合JMM(Java内存模型)深入学习并发编程理论