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