避免脏读的关键是保障共享变量的可见性,可选用volatile(适用于单写多读)、synchronized/Lock(读写互斥,兼具原子性与有序性)、AtomicInteger等原子类(CAS实现无锁原子操作),或从源头消除共享(不可变对象、ThreadLocal、消息传递)。

要避免多线程环境下的脏读,关键不是“手动同步主内存”,而是让 JVM 按照 Java 内存模型(JMM)的规则,确保一个线程对共享变量的修改对其他线程**及时可见**。脏读的本质是:线程 B 读到了线程 A 已修改但尚未刷新到主内存、或未被 B 看到的旧值。解决方向是保障可见性,必要时兼顾原子性和有序性。
用 volatile 保证基本可见性
适用于“一个写、多个读”且写操作本身是原子的场景(如布尔标志、状态开关、简单赋值):
- 声明变量时加上 volatile,例如
private static volatile boolean running = true; - 它强制每次读都从主内存取最新值,每次写都立即刷回主内存,跳过工作内存缓存
- 同时禁止相关指令重排,维持程序逻辑顺序
- 注意:volatile 不能解决 i++ 这类复合操作的原子性问题,仅靠它无法避免计数器丢失更新
用 synchronized 或 Lock 实现读写互斥
适用于需要“读-改-写”原子执行,或多个写线程竞争同一变量的场景:
- 对所有访问该变量的代码块(包括读和写)加同一把锁,例如用
synchronized(this)或synchronized修饰方法 - 进入同步块时,JVM 会强制从主内存刷新变量副本;退出时,强制将修改写回主内存
- 天然具备原子性 + 可见性 + 有序性,是最稳妥的通用方案
- 缺点是可能带来阻塞和性能开销,需合理控制临界区粒度
用 AtomicInteger 等原子类替代普通变量
适用于整型计数、状态位更新等高频、低竞争场景:
- AtomicInteger 内部 value 字段是 volatile 的,保证了读写可见性
- 核心操作(如
incrementAndGet())基于 CAS,硬件级保证原子性,无锁且高效 - 比 synchronized 轻量,比单纯 volatile 更安全,是“可见性+原子性”的一体化选择
- 不适用于复杂对象或需要多步协调的业务逻辑
避免共享可变状态,从源头消除风险
最彻底的方式不是同步,而是减少甚至消除跨线程共享:
- 优先使用不可变对象(final 字段、无 setter)
- 用 ThreadLocal 为每个线程提供独立副本,适合上下文传递(如用户身份、事务ID)
- 通过消息传递(如 BlockingQueue)代替直接共享变量,实现线程间解耦
- 设计时考虑无状态服务,把状态外移到 Redis、数据库等外部存储中统一管理
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











