一、引子:为什么你的多线程程序总在翻车?
1.1 血淋淋的并发事故现场
public class ConcurrencyCrash {
private int count = 0;
public void increment() {
for (int i = 0; i < 10000; i++) {
count++; // 经典错误姿势
}
}
// 同时启动10个线程执行increment()
// 最终结果永远不可能是10*10000=100000
}
运行结果:输出98231(每次运行结果不确定)
二、罪状解析:并发问题的三种面部特征
2.1 原子性(Atomicity)之殇
线程1读count=0
线程1加1运算
线程2读count=0
线程2加1运算
写回count=1
写回count=1
核心要素:不可分割的操作序列
2.2 可见性(Visibility)迷雾
public class VisibilityTrap {
boolean isRunning = true;
void shutdown() {
isRunning = false; // 修改不可见!!
}
void worker() {
while (isRunning) {
// 永远无法退出的工作循环
}
}
}
问题根源:JMM内存模型导致的缓存不可见
2.3 有序性(Ordering)黑洞
public class InstructionReordering {
int a = 0;
boolean flag = false;
void writer() {
a = 1; // (1)
flag = true; // (2) 可能被重排序!
}
void reader() {
if (flag) { // (3)
System.out.println(a); // 可能输出0
}
}
}
幕后黑手:编译器优化与指令重排序
三、技术解剖:三大特质的终极解决方案
3.1 原子性保卫战
3.1.1 synchronized锁机制
public class AtomicGuard {
private int counter = 0;
public synchronized void safeIncrement() {
counter++;
}
}
锁升级过程:
3.1.2 CAS无锁方案
AtomicInteger atomicCount = new AtomicInteger(0); atomicCount.getAndIncrement(); // 原子自增
CAS原理: Compare And Swap -> V == E ? V = N : 重试
3.2 可见性拯救行动
3.2.1 volatile关键字
public class VisibilitySolution {
volatile boolean flag = false;
void toggle() {
flag = !flag; // 写操作立即对其他线程可见
}
}
内存屏障:
3.2.2 锁的附带效应
synchronized(lock) {
// 临界区内的修改对其他线程可见
}
3.3 有序性镇魂歌
3.3.1 happens-before原则
程序顺序规则
锁规则
volatile规则
传递性规则
3.3.2 final关键字
public class SafePublication {
private final int immutableValue;
public SafePublication(int value) {
this.immutableValue = value; // 正确构造的对象安全发布
}
}
四、真实战场:单例模式双重检测
4.1 错误姿势
public class DoubleCheckLock {
private static Singleton instance;
public static Singleton getInstance() {
if (instance == null) { // 第一次检测
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // 问题根源在此
}
}
}
return instance;
}
}
关键风险:对象半初始化状态
4.2 正确实现
public class SafeDoubleCheck {
private volatile static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (SafeDoubleCheck.class) {
if (instance == null) {
instance = new Singleton();
// 1.分配内存空间
// 2.初始化对象
// 3.设置指针指向(volatile写)
}
}
}
return instance;
}
}
内存屏障示意:
五、原子性可视化诊断
5.1 使用JConsole观测
5.2 Arthas在线追踪
watch com.example.ConcurrencyDemo counter \
'{params,returnObj,throwExp}' \
-x 3 -b -n 5
六、三大特性的协同关系
| 特性 | 破坏场景 | 保障手段 | 适用场景 |
|---|---|---|---|
| 原子性 | i++并发写入 | synchronized/原子类 | 状态统计 |
| 可见性 | 修改值不可见 | volatile/锁机制 | 标志位控制 |
| 有序性 | 指令重排序导致异常 | volatile/happens-before原则 | 延迟初始化 |
七、避坑指南:六大常见误区
-
滥用volatile:"volatile万能论"错误认知
-
过度同步:同步块粒度过粗导致性能下降
-
伪原子操作:复合操作的伪装性
-
final的误用:未正确发布final对象
-
ThreadLocal陷阱:线程封闭被破坏
-
单例模式误判:未考虑反射攻击
八、高阶武器:JUC工具包精选
8.1 Atomic家族
AtomicIntegerArray AtomicReference LongAdder // 高性能计数器
8.2 Lock体系
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); lock.readLock().lock(); lock.writeLock().lock();
8.3 线程安全集合
ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>(); CopyOnWriteArrayList<String> logList = new CopyOnWriteArrayList<>();
九、设计启示录
9.1 线程封闭策略
-
Stack Confinement(栈封闭)
-
ThreadLocal模式
-
不可变对象
9.2 安全发布模式
-
静态初始化
-
volatile修饰
-
安全容器存储
-
final正确使用
9.3 Java内存模型启示
十、终极思考:如何平衡三大特性?
黄金法则:
-
优先使用无锁设计
-
最小化同步范围
-
理解底层内存语义
-
善用工具验证假设
1690

被折叠的 条评论
为什么被折叠?



