volatile关键字
volatile 关键字
volatile 修饰字段,建立线程间的可见性和特定有序性保证。它不提供互斥,不能自动把复合操作变成原子操作。
代码块收起展开
private volatile boolean running = true;它不能修饰局部变量、方法或类。
可见性
一个线程对 volatile 字段的写,happens-before 后续其他线程对该字段的读。因此读线程也能看到该次 volatile 写之前完成的普通写入。
代码块收起展开
class Task {
private int result;
private volatile boolean ready;
void produce() {
result = 42;
ready = true;
}
void consume() {
if (ready) {
System.out.println(result);
}
}
}“刷新到主内存”只是近似说法,正式依据是 Java 内存模型的 happens-before 规则。
有序性
volatile 读写具有内存屏障语义,限制相关操作跨越 volatile 边界重排。它不是禁止所有重排,而是保证其他线程不能观察到违反内存模型的顺序。
不保证复合操作原子性
代码块收起展开
private volatile int count;
void increment() {
count++; // 读、加、写,不是原子操作
}多个线程仍会丢失更新。应使用 AtomicInteger 或 synchronized:
代码块收起展开
private final AtomicInteger count = new AtomicInteger();
void increment() {
count.incrementAndGet();
}典型场景
停止标志
代码块收起展开
private volatile boolean stopped;
void runLoop() {
while (!stopped) {
doOneUnitOfWork();
}
}
void stop() {
stopped = true;
}若线程阻塞在 I/O 或等待中,仅修改标志不能唤醒它,还需要中断等机制。
发布不可变快照
代码块收起展开
private volatile Config config = Config.defaults();
void reload() {
config = loadNewImmutableConfig();
}volatile List 只保证列表引用可见,不会让列表内部修改自动线程安全。
双重检查锁
代码块收起展开
class Singleton {
private static volatile Singleton instance;
static Singleton getInstance() {
Singleton result = instance;
if (result == null) {
synchronized (Singleton.class) {
result = instance;
if (result == null) {
result = new Singleton();
instance = result;
}
}
}
return result;
}
}volatile 防止对象引用在初始化效果完整可见之前被其他线程观察。实际单例通常优先使用枚举或静态内部类持有者模式。
选择原则
只有当写操作不依赖旧值,且字段不参与必须整体一致的多变量不变量时,才适合 volatile。否则使用锁、原子类或并发容器。