在 Java 并发编程中,可见性、有序性、原子性是并发错误的三大核心根源。
✅ 一、如何解决 可见性问题?
🧠 问题回顾:
- 一个线程修改了共享变量,其他线程看不到(缓存没有刷新)。
🔧 Java 解决方式:
机制 | 说明 |
---|
volatile | 禁止线程缓存变量,修改立即写入主内存;读取强制从主内存读。 ✅轻量方式 |
synchronized | 加锁会刷新工作内存,进入同步块前读取主内存,退出时写回主内存 |
final | 构造函数安全发布(对象构造完成前不能被其他线程看到) |
JMM 的 happens-before 规则 | Java 的内存模型定义了一系列可见性保证规则 |
✅ 二、如何解决 有序性问题?
🧠 问题回顾:
- JVM 和 CPU 会重排序指令,导致多线程执行顺序不一致。
🔧 Java 解决方式:
机制 | 说明 |
---|
volatile | 禁止重排序(对读写操作的指令不会和前后的指令重排序) |
synchronized | 锁具备内存屏障效果,进入和退出同步块都包含“顺序约束” |
happens-before 语义 | JMM 规定了某些操作之间的“先发生于”关系,编译器/CPU 会保持顺序 |
📝 举例(重排序引发的问题):
int a = 0;
boolean flag = false;
Thread A:
a = 1;
flag = true;
Thread B:
if (flag) {
System.out.println(a);
}
使用 volatile flag
可禁止重排序。
✅ 三、如何解决 原子性问题?
🧠 问题回顾:
- 多线程并发修改变量时,中间过程被打断,导致结果错误。
🔧 Java 解决方式:
机制 | 说明 |
---|
synchronized | 让某段代码在任意时刻只允许一个线程执行,从而保证操作的原子性 |
ReentrantLock | 可替代 synchronized,有更强的功能(可中断、公平锁、tryLock) |
AtomicInteger 等原子类 | JDK 提供的原子操作类,底层基于 CAS ,可高性能地支持线程安全的加减等操作 |
数据结构并发容器(如 ConcurrentHashMap ) | 内部采用分段锁或CAS原语,支持高性能并发访问 |
✅ 总结
并发问题 | 典型现象 | Java 解决机制 |
---|
可见性 | 线程 A 修改变量,线程 B 看不到 | volatile 、synchronized 、final 、JMM happens-before |
有序性 | 指令被重排序,行为反直觉 | volatile 、synchronized 、JMM 顺序规则 |
原子性 | 多线程修改共享变量,结果错误 | synchronized 、Lock 、AtomicXXX 、并发容器 |