hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶
面试官:详解原子性、可见性、有序性
在并发编程中,原子性(Atomicity)、可见性(Visibility) 和 有序性(Ordering) 是确保多线程程序正确性的三大核心特性。它们共同解决了多线程环境下的数据竞争、状态不一致等问题。
1. 原子性(Atomicity)
定义
原子性指一个操作不可分割,要么完全执行成功,要么完全不执行,中间状态对其他线程不可见。
问题场景
- 复合操作的非原子性:例如
i++
操作实际分为三步:读取i
、计算i+1
、写回i
。多线程下,多个线程可能交替执行这些步骤,导致最终结果不符合预期。 - 示例:
若两个线程同时执行int i = 0; // 线程1和线程2同时执行以下代码 i++; // 实际可能丢失部分更新
i++
,期望结果应为2
,但可能得到1
。
解决方案
-
锁机制(如
synchronized
):synchronized (lock) {i++; }
通过互斥锁确保同一时间只有一个线程执行代码块。
-
原子类(如
AtomicInteger
):AtomicInteger atomicInt = new AtomicInteger(0); atomicInt.incrementAndGet(); // CAS 操作保证原子性
底层基于 CAS(Compare-And-Swap) 实现,无锁且高效。
-
基本类型的原子读写:
Java 中除long
和double
外的基本类型(如int
、boolean
)的读写是原子的。
注意:32 位 JVM 中long/double
的非原子性可能导致“写撕裂”(写入半个字)。
2. 可见性(Visibility)
定义
可见性指当一个线程修改共享变量后,其他线程能立即看到修改后的值。
问题场景
- 工作内存与主内存不一致:
线程操作变量时,可能从工作内存(CPU 缓存)读取副本,而非主内存。未同步时,修改可能对其他线程不可见。 - 示例:
线程2修改boolean flag = true; // 线程1 while (flag) { /* ... */ } // 可能死循环,即使线程2修改了flag // 线程2 flag = false;
flag
后,线程1可能无法及时感知。
解决方案
-
volatile
关键字:volatile boolean flag = true;
volatile
强制所有读写直接操作主内存,并通过内存屏障禁止重排序。 -
锁机制(如
synchronized
、Lock
):
锁的释放会强制将工作内存刷新到主内存,锁的获取会清空工作内存,从主内存重新加载变量。 -
final
关键字:
正确构造的对象(无this
逃逸),其final
字段的初始化值对所有线程可见。final int x = 42; // 其他线程看到x一定是42
3. 有序性(Ordering)
定义
有序性指程序执行的顺序按代码的先后顺序执行,但编译器和处理器可能重排序指令以提高性能。
问题场景
- 指令重排序导致逻辑错误:
例如双重检查锁定(DCL)单例模式中,可能返回未完全初始化的对象。// 错误示例 if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); // 可能发生重排序}} }
new Singleton()
的步骤可能被重排序为:分配内存 → 返回引用 → 初始化对象。其他线程可能拿到未初始化的实例。
解决方案
-
volatile
关键字:private static volatile Singleton instance;
volatile
禁止指令重排序,确保对象完全初始化后才赋值给引用。 -
Happens-Before 规则:
Java 内存模型定义的操作顺序规则,包括:- 程序顺序规则:单线程内代码顺序执行。
- 锁规则:解锁先于后续加锁。
volatile
规则:写操作先于后续读操作。- 传递性:若 A 先于 B,B 先于 C,则 A 先于 C。
-
内存屏障:
JVM 通过插入内存屏障(如LoadLoad
、StoreStore
)限制编译器和 CPU 的重排序。
三者的关系与对比
特性 | 定义 | 典型问题 | 解决工具 |
---|---|---|---|
原子性 | 操作不可分割 | 竞态条件(如 i++ 错误) | synchronized 、原子类、CAS |
可见性 | 修改后其他线程立即可见 | 脏读、死循环 | volatile 、synchronized 、final |
有序性 | 代码顺序执行,禁止重排序 | 对象未初始化、逻辑错误 | volatile 、Happens-Before、内存屏障 |
实际应用示例
1. 原子性:银行转账
public class Account {private int balance;// 使用 synchronized 保证原子性public synchronized void transfer(Account target, int amount) {if (this.balance >= amount) {this.balance -= amount;target.balance += amount;}}
}
2. 可见性:状态标志
public class Server {private volatile boolean isRunning = true;public void stop() { isRunning = false; }public void serve() {while (isRunning) { /* 处理请求 */ }}
}
3. 有序性:单例模式(DCL)
public class Singleton {private static volatile Singleton instance;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); // 禁止重排序}}}return instance;}
}