一、引子:为什么你的多线程程序总在翻车?
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内存模型启示
十、终极思考:如何平衡三大特性?
黄金法则:
-
优先使用无锁设计
-
最小化同步范围
-
理解底层内存语义
-
善用工具验证假设