使用volatile确保简单变量可见性,synchronized和Lock保证原子性与可见性,Atomic类实现无锁线程安全,合理选择取决于场景需求。

在Java中,线程间数据可见性问题源于每个线程可能拥有共享变量的本地副本(如CPU缓存),导致一个线程对变量的修改不能及时被其他线程看到。要确保数据在多线成品间正确可见,需要使用Java内存模型(JMM)提供的同步机制。
使用volatile关键字
volatile 是处理简单共享变量可见性的最轻量级方式。当一个变量被声明为 volatile,Java会保证:
- 对该变量的写操作对后续读操作的线程立即可见
- 禁止指令重排序优化,确保执行顺序符合预期
适合场景:状态标志位、双检锁中的实例引用等。注意 volatile 不保证复合操作的原子性(如 i++)。
示例:private volatile boolean running = true;
public void stop() {
running = false;
}
public void runLoop() {
while (running) {
// 执行任务
}
}
使用synchronized关键字
synchronized 不仅保证代码块的原子性,还建立“happens-before”关系,从而保障可见性。
立即学习“Java免费学习笔记(深入)”;
进入 synchronized 块前,线程会清空本地变量副本,从主内存重新读取;退出时,修改会被刷新回主内存。
- 可用于方法或代码块
- 多个线程竞争同一把锁时,能保证数据的一致读写
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
使用java.util.concurrent.atomic包
Atomic类(如 AtomicInteger、AtomicBoolean)基于CAS(Compare-And-Swap)实现,既保证可见性也保证原子性。
- 内部使用 volatile 保证可见性
- 通过Unsafe类调用底层CPU指令实现原子更新
- 适用于计数器、状态切换等高频读写场景
private AtomicInteger counter = new AtomicInteger(0);
public void increment() {
counter.incrementAndGet();
}
public int getValue() {
return counter.get();
}
使用显式内存屏障(Lock API)
ReentrantLock、ReadWriteLock 等显式锁在释放锁时会强制将修改刷回主内存,获取锁时会失效本地缓存。
它们不仅提供互斥,也建立 happens-before 关系,确保线程间的数据可见。
相比 synchronized,更灵活,支持中断、超时、公平性等特性。
基本上就这些。合理选择机制取决于具体场景:volatile 用于状态标志,synchronized 和 Lock 用于复合操作保护,Atomic 类用于无锁原子操作。关键是理解每种方式背后的内存语义。










