Java 并发编程三大核心特性(原子性、可见性、有序性)
·
一、原子性(Atomicity)
定义
一个操作要么全部执行成功,要么全部不执行,不会被线程切换打断。
举例
i++;
看似一行代码,其实包含 3 个步骤:
- 读取 i
- i + 1
- 写回 i
在多线程下可能出现:
线程A 读取 0
线程B 读取 0
线程A 写回 1
线程B 写回 1
最终结果是 1,而不是 2 —— 这叫 竞态条件(Race Condition)
如何保证原子性?
synchronizedLockAtomicInteger- CAS(Compare And Swap)
例如:
AtomicInteger i = new AtomicInteger(0);
i.incrementAndGet();
底层使用 CAS + 自旋 保证原子性。
二、可见性(Visibility)
定义
一个线程对共享变量的修改,其他线程能够立即看到。
为什么会出现可见性问题?
因为 Java 内存模型(JMM):
- 线程有自己的 工作内存
- 共享变量在 主内存
线程操作流程:
主内存 → 工作内存 → 运算 → 写回主内存
如果线程A修改变量,但没及时刷新到主内存,线程B就看不到。
典型问题
boolean flag = true;
Thread A:
while(flag) {
}
Thread B:
flag = false;
可能死循环!
因为线程A可能一直用自己的缓存。
如何保证可见性?
1. volatile
volatile boolean flag = true;
作用:
- 保证可见性
- 禁止指令重排序(部分)
但不保证原子性!
2. synchronized
- 解锁前必须刷新主内存
- 加锁时必须读取主内存
三、有序性(Ordering)
定义
程序执行顺序按照代码顺序执行。
问题是:CPU 和 JVM 会进行指令重排序优化。
指令重排序的三种情况
- 编译器优化重排
- CPU 指令重排
- 内存系统重排
举个经典例子(单例 DCL)
instance = new Singleton();
这行代码可能会被重排成:
- 分配内存
- 指向内存
- 初始化对象
如果发生 1 → 3 → 2 重排
线程A执行到步骤2时,线程B可能拿到一个“未初始化完成的对象”。
如何保证有序性?
volatile
- 插入 内存屏障(Memory Barrier)
- 禁止重排序
三者对比总结
| 特性 | 解决问题 | 关键点 | 典型实现 |
|---|---|---|---|
| 原子性 | 操作不可被打断 | 整体执行 | synchronized / CAS |
| 可见性 | 修改能被其他线程看到 | 主内存同步 | volatile |
| 有序性 | 禁止重排序 | 内存屏障 | volatile |
面试高频连问
1. volatile 保证原子性吗?
❌ 不保证
只保证 可见性 + 有序性
2. synchronized 保证什么?
✅ 原子性
✅ 可见性
✅ 有序性
3. volatile 和 synchronized 的本质区别?
| volatile | synchronized |
|---|---|
| 轻量级 | 重量级 |
| 不加锁 | 加锁 |
| 不能保证复合操作原子性 | 可以 |
一句话理解
- 原子性:不能被“打断”
- 可见性:不能被“看不到”
- 有序性:不能被“乱序”
更多推荐




所有评论(0)