volatile不能实现完整发布-订阅机制,仅适用于“单次状态变更+简单唤醒”场景,如“数据已就绪”;它保证可见性和禁止重排序,但不提供原子性、互斥或等待-唤醒语义。

volatile 本身不能直接实现完整的发布-订阅机制,但它可以作为轻量级通知的底层基础——适用于“单次状态变更 + 简单唤醒”场景,比如“任务已启动”“数据已就绪”“开关已关闭”这类一次性信号。
为什么 volatile 能用于简易通知
volatile 关键字保证两点:一是写操作对其他线程立即可见;二是禁止指令重排序(针对该变量的读写)。这使得一个线程修改 volatile 变量后,另一个线程能马上看到新值,从而触发响应逻辑。
注意:它不提供原子性(如 count++ 不安全)、不保证临界区互斥、也不能替代 wait/notify 或 Condition 的等待-唤醒语义。所以只适合“检查 → 响应”这种轮询式或事件驱动的简单协作。
用 volatile 实现“数据就绪”通知
典型场景:生产者写完数据后设置标志,消费者轮询该标志并消费。
- 定义一个 volatile 布尔变量表示就绪状态
- 生产者完成数据准备后,将标志设为
true - 消费者在循环中检查该标志,为
true时读取共享数据(注意:数据本身也需保证可见性,建议用 final 字段封装,或同样用 volatile)
public class SimplePublisher {
private volatile boolean dataReady = false;
private final String payload = "Hello from publisher";
public void publish() {
// 模拟准备数据(注意:payload 是 final,保证初始化完成可见)
dataReady = true; // volatile 写,通知消费者
}
public boolean isDataReady() {
return dataReady;
}
public String getPayload() {
return payload;
}
}
// 消费者(可运行在另一线程)
while (!publisher.isDataReady()) {
Thread.sleep(10); // 简单休眠避免忙等(实际可用 LockSupport.parkNanos)
}
System.out.println(publisher.getPayload()); // 安全读取
配合 wait/notify 实现更高效的唤醒
纯 volatile 轮询浪费 CPU。可以在 volatile 标志变化后,用 synchronized + notify() 主动唤醒等待线程,兼顾效率与简洁性。
- volatile 变量作为“是否需要唤醒”的轻量判断入口
- 真正等待仍走 Object.wait(),避免空转
- 写入 volatile 后立刻同步块内 notify,确保唤醒不丢失
public class NotifiablePublisher {
private volatile boolean notified = false;
private final Object lock = new Object();
public void signal() {
notified = true;
synchronized (lock) {
lock.notify(); // 唤醒可能正在 wait 的线程
}
}
public void waitForSignal() throws InterruptedException {
synchronized (lock) {
while (!notified) {
lock.wait(); // 等待通知
}
}
}
}
关键注意事项
- volatile 仅保障单个变量的可见性,若需多个字段协同(如状态 + 数据 + 时间戳),应封装成
final对象并用 volatile 引用它 - 不要依赖 volatile 实现计数器或累加逻辑(
volatile int count的count++非原子) - 高并发、多订阅者、需解耦或回调的场景,请用
java.util.concurrent.CopyOnWriteArrayList+ExecutorService或成熟的事件总线(如 Guava EventBus) - volatile 通知是“最多一次”,无法重放或持久化,也不支持取消订阅
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











