Java volatile 可见性:为什么改了标志位线程却读不到
主线程改了
boolean标志,工作线程却读不到,本质是 volatile 缺失导致的可见性问题。本文讲清楚 JMM 工作内存/主内存模型、volatile 的可见性与有序性,以及它不保证原子性的边界。
一、问题案例
一个后台任务用boolean标志控制退出,主线程把标志设成false,工作线程的while循环却一直退不出来。
classWorkerimplementsRunnable{privatebooleanrunning=true;// ❌ 没加 volatilepublicvoidrun(){while(running){/* 干活 */}}publicvoidstop(){running=false;}}二、原理详解:JMM 与可见性
Java 内存模型(JMM)里,每个线程有自己的工作内存(高速缓存),共享变量在主内存。
running没加 volatile → 工作线程可能把它缓存进自己的工作内存;- 主线程调
stop()改的是主内存的值; - 工作线程读的是自己工作内存的旧值 → 永远看不到变化。
volatile干两件事:
- 保证可见性:每次读写都直连主内存,改了就立刻能被别的线程看到;
- 禁止指令重排:保证写读顺序,防止拿到「半初始化」的对象。
但 volatile 不保证原子性。i++这种「读-改-写」复合操作,多线程下依然会丢更新。
三、实战代码
privatevolatilebooleanrunning=true;一行搞定标志位可见性问题。
四、常见踩坑
i++不是原子的:要用AtomicInteger或锁。- 双重检查锁(DCL)单例要加 volatile:
instance = new Xxx()不是原子操作,可能发生指令重排,导致别的线程拿到未初始化完的对象;instance加 volatile 可以禁止这种重排。
五、总结
- 多线程共享的标志位,加 volatile。
- volatile 保证可见性 + 有序性,不保证原子性。
- 复合操作(
i++、check-then-act)交给AtomicX或锁。
我是无羡(小剑),全栈偏后端的独立开发者。
作品集:无羡 · 独立开发者作品集
如果对你有帮助,欢迎点赞、收藏、关注。