volatile 关键字
概述
volatile
关键字是Java提供的轻量级同步机制,用于确保变量的内存可见性和禁止指令重排序。当一个变量被声明为volatile时,对该变量的读写操作都会直接在主内存中进行。
语法格式
volatile int variable;
volatile boolean flag;
volatile Object reference;
核心特性
1. 内存可见性
volatile变量的修改对所有线程立即可见,解决了多线程环境下的数据一致性问题。
2. 禁止指令重排序
编译器和处理器不会对volatile变量的读写操作进行重排序优化。
基本用法
内存可见性示例
public class VisibilityExample {
// 不使用volatile的情况
private boolean stopFlag = false;
// 使用volatile的情况
private volatile boolean volatileStopFlag = false;
// 演示普通变量可见性问题
public void demonstrateVisibilityProblem() {
Thread writerThread = new Thread(() -> {
try {
Thread.sleep(1000);
stopFlag = true; // 修改标志位
System.out.println("Writer线程:设置stopFlag = true");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread readerThread = new Thread(() -> {
while (!stopFlag) {
// 可能会无限循环,因为看不到stopFlag的变化
}
System.out.println("Reader线程:检测到stopFlag = true,退出循环");
});
writerThread.start();
readerThread.start();
try {
writerThread.join();
// 注意:readerThread可能不会退出
readerThread.join(3000); // 最多等待3秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 演示volatile变量的可见性
public void demonstrateVolatileVisibility() {
Thread writerThread = new Thread(() -> {
try {
Thread.sleep(1000);
volatileStopFlag = true; // 修改volatile标志位
System.out.println("Writer线程:设置volatileStopFlag = true");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread readerThread = new Thread(() -> {
while (!volatileStopFlag) {
// volatile确保能及时看到变化
}
System.out.println("Reader线程:检测到volatileStopFlag = true,退出循环");
});
writerThread.start();
readerThread.start();
try {
writerThread.join();
readerThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
VisibilityExample example = new VisibilityExample();
System.out.println("=== 演示普通变量可见性问题 ===");
example.demonstrateVisibilityProblem();
System.out.println("\n=== 演示volatile变量的可见性 ===");
example.demonstrateVolatileVisibility();
}
}
状态标志模式
public class StatusFlagExample {
private volatile boolean isReady = false;
private volatile boolean isShutdown = false;
// 工作线程
public void workerThread() {
Thread worker = new Thread(() -> {
System.out.println("工作线程:等待就绪信号...");
// 等待就绪信号
while (!isReady) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
System.out.println("工作线程:开始工作");
// 执行工作直到收到关闭信号
while (!isShutdown) {
System.out.println("工作线程:正在工作...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
System.out.println("工作线程:收到关闭信号,停止工作");
});
worker.start();
// 主线程控制工作流程
try {
Thread.sleep(1000);
setReady(true); // 发送就绪信号
Thread.sleep(3000);
shutdown(); // 发送关闭信号
worker.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void setReady(boolean ready) {
this.isReady = ready;
System.out.println("主线程:设置就绪状态为 " + ready);
}
public void shutdown() {
this.isShutdown = true;
System.out.println("主线程:发送关闭信号");
}
public static void main(String[] args) {
StatusFlagExample example = new StatusFlagExample();
example.workerThread();
}
}
单例模式的双重检查锁定
public class Singleton {
// 使用volatile确保多线程环境下的正确性
private static volatile Singleton instance;
private Singleton() {
// 私有构造器
}
// 双重检查锁定(Double-Checked Locking)
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // volatile确保这里不会被重排序
}
}
}
return instance;
}
}
// 演示为什么需要volatile
public class SingletonTest {
public static void main(String[] args) {
// 创建多个线程同时获取单例
Runnable task = () -> {
Singleton singleton = Singleton.getInstance();
System.out.println(Thread.currentThread().getName() +
": " + singleton.hashCode());
};
for (int i = 0; i < 10; i++) {
new Thread(task, "Thread-" + i).start();
}
}
}
生产者-消费者模式的简单实现
public class SimpleProducerConsumer {
private volatile int count = 0;
private volatile boolean hasData = false;
private final int maxCount = 10;
// 生产者
public void producer() {
Thread producerThread = new Thread(() -> {
for (int i = 1; i <= maxCount; i++) {
// 等待消费者处理完数据
while (hasData) {
Thread.yield();
}
count = i;
hasData = true;
System.out.println("生产者:生产数据 " + count);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
producerThread.start();
}
// 消费者
public void consumer() {
Thread consumerThread = new Thread(() -> {
for (int i = 1; i <= maxCount; i++) {
// 等待生产者生产数据
while (!hasData) {
Thread.yield();
}
System.out.println("消费者:消费数据 " + count);
hasData = false;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
consumerThread.start();
}
public static void main(String[] args) {
SimpleProducerConsumer example = new SimpleProducerConsumer();
example.producer();
example.consumer();
try {
Thread.sleep(5000); // 等待执行完成
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
配置更新场景
public class ConfigurationManager {
// 配置信息使用volatile确保更新的可见性
private volatile String databaseUrl = "jdbc:mysql://localhost:3306/db";
private volatile int connectionTimeout = 5000;
private volatile boolean debugMode = false;
// 读取配置(高频操作)
public String getDatabaseUrl() {
return databaseUrl;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public boolean isDebugMode() {
return debugMode;
}
// 更新配置(低频操作)
public void updateDatabaseUrl(String newUrl) {
this.databaseUrl = newUrl;
System.out.println("配置更新:数据库URL = " + newUrl);
}
public void updateConnectionTimeout(int timeout) {
this.connectionTimeout = timeout;
System.out.println("配置更新:连接超时 = " + timeout);
}
public void updateDebugMode(boolean debug) {
this.debugMode = debug;
System.out.println("配置更新:调试模式 = " + debug);
}
// 模拟配置使用者
public void startWorkers() {
// 工作线程1:数据库操作
Thread dbWorker = new Thread(() -> {
for (int i = 0; i < 10; i++) {
String url = getDatabaseUrl();
int timeout = getConnectionTimeout();
System.out.println("DB Worker: 连接 " + url + ",超时 " + timeout + "ms");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}, "DB-Worker");
// 工作线程2:日志记录
Thread logWorker = new Thread(() -> {
for (int i = 0; i < 10; i++) {
boolean debug = isDebugMode();
if (debug) {
System.out.println("Log Worker: 调试信息 - 操作" + i);
} else {
System.out.println("Log Worker: 普通日志 - 操作" + i);
}
try {
Thread.sleep(700);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}, "Log-Worker");
// 配置更新线程
Thread configUpdater = new Thread(() -> {
try {
Thread.sleep(2000);
updateDebugMode(true);
Thread.sleep(2000);
updateConnectionTimeout(8000);
Thread.sleep(2000);
updateDatabaseUrl("jdbc:mysql://newserver:3306/newdb");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Config-Updater");
dbWorker.start();
logWorker.start();
configUpdater.start();
try {
dbWorker.join();
logWorker.join();
configUpdater.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
ConfigurationManager manager = new ConfigurationManager();
manager.startWorkers();
}
}
volatile的局限性
1. 非原子操作
public class VolatileLimitation {
private volatile int counter = 0;
// 这个操作不是原子的,volatile无法保证线程安全
public void incrementCounter() {
counter++; // 相当于:counter = counter + 1(读取、计算、写入三个步骤)
}
// 正确的做法:使用synchronized
public synchronized void safeIncrementCounter() {
counter++;
}
// 或者使用AtomicInteger
private final AtomicInteger atomicCounter = new AtomicInteger(0);
public void atomicIncrementCounter() {
atomicCounter.incrementAndGet();
}
// 演示volatile不能保证原子性
public void demonstrateNonAtomicity() {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
incrementCounter(); // 不安全的操作
}
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("预期结果: 2000");
System.out.println("实际结果: " + counter); // 可能小于2000
System.out.println("原子计数器: " + atomicCounter.get()); // 如果也执行了原子操作
}
public static void main(String[] args) {
VolatileLimitation example = new VolatileLimitation();
example.demonstrateNonAtomicity();
}
}
2. 复合操作的问题
public class CompoundOperationIssue {
private volatile int min = 0;
private volatile int max = 0;
// 这种复合操作在多线程环境下可能出现问题
public void updateRange(int newMin, int newMax) {
// 问题:这两个赋值操作之间可能被其他线程中断
min = newMin;
max = newMax;
// 如果另一个线程在这两行代码之间读取min和max,
// 可能看到不一致的状态
}
// 正确的做法:使用synchronized保证原子性
public synchronized void safeUpdateRange(int newMin, int newMax) {
min = newMin;
max = newMax;
}
public void readRange() {
int currentMin = min;
int currentMax = max;
System.out.println("范围: [" + currentMin + ", " + currentMax + "]");
// 可能出现min > max的不一致状态
if (currentMin > currentMax) {
System.out.println("警告:检测到不一致状态!");
}
}
}
使用场景
1. 状态标志
public class StatusFlag {
private volatile boolean initialized = false;
private volatile boolean running = false;
private volatile boolean error = false;
public void setInitialized() { initialized = true; }
public void setRunning() { running = true; }
public void setError() { error = true; }
public boolean isInitialized() { return initialized; }
public boolean isRunning() { return running; }
public boolean hasError() { return error; }
}
2. 配置信息
public class Configuration {
private volatile String serverUrl;
private volatile int timeout;
private volatile boolean enableCache;
// 配置更新方法
public void updateConfig(String url, int timeout, boolean cache) {
this.serverUrl = url;
this.timeout = timeout;
this.enableCache = cache;
}
}
3. 双重检查锁定模式
public class LazyInitialization {
private volatile ExpensiveObject instance;
public ExpensiveObject getInstance() {
if (instance == null) {
synchronized (this) {
if (instance == null) {
instance = new ExpensiveObject();
}
}
}
return instance;
}
private class ExpensiveObject {
// 昂贵的对象初始化
}
}
最佳实践
仅用于状态标志:
- volatile最适合简单的状态标志
- 避免在需要原子操作的场景使用
读多写少的场景:
- 适合配置信息、状态变量等读多写少的场景
- 写操作通常是单线程的
与synchronized结合:
- 在需要原子性的地方使用synchronized
- volatile用于状态发布
考虑使用Atomic类:
- 对于计数器等场景,使用AtomicInteger等原子类
- 对于引用类型,使用AtomicReference
注意事项
volatile不能替代synchronized:
- volatile只保证可见性,不保证原子性
- 复合操作仍需要synchronized
性能考虑:
- volatile变量的读写比普通变量慢
- 但比synchronized块快得多
使用场景限制:
- 不适合复杂的同步需求
- 不能用于同步多个变量的状态
编程模型:
- 适合一个线程写,多个线程读的模式
- 避免多个线程同时写入
volatile关键字是Java并发编程中的重要工具,但需要理解其局限性并在合适的场景中使用。