Skip to content

并发编程技术栈总览

蒸馏自 B40-资源/语雀-Java开发/多线程与高并发/(26 篇:多线程基础 156KB + Java 中的锁 66KB + 王宝令《Java 并发编程实战》理论基础 13 篇 + JUC 5 篇 + Happens-Before + 逃逸分析)。本页是并发域的全局地图,细节见各概念卡。

一句话机制

并发编程的所有复杂性,都源于可见性、原子性、有序性三大问题;Java 用 JMM(内存模型)规范可见性/有序性,用锁/CAS 解决原子性,再往上封装出 AQS、线程池等工具。 理解并发 = 先吃透「三大问题」,再沿着「JMM → 锁/CAS → AQS → 线程池」逐层往上。

并发全景分层图

解决什么关键机制详情页
三大问题Bug 的根可见性(缓存)/原子性(切换)/有序性(重排)概念卡片:Java内存模型与Happens-Before
JMM按需禁用缓存/重排volatile / synchronized / final + Happens-Before 规则概念卡片:Java内存模型与Happens-Before
锁 / CAS原子性synchronized 锁升级 / ReentrantLock / CAS 乐观锁概念卡片:synchronized锁升级与Monitor · 概念卡片:CAS乐观锁与原子类
AQS锁的通用框架state + FIFO 队列 + park/unpark概念卡片:AQS同步器框架
线程池线程复用ThreadPoolExecutor 参数 + 执行流程概念卡片:Java线程池ThreadPoolExecutor

三大问题一句话

问题根因典型表现
可见性CPU 多级缓存线程 A 改了 flag,线程 B 读不到,死循环
原子性线程切换打断复合操作count++ 两步之间被切换,结果丢失
有序性编译器/CPU 指令重排DCL 单例拿到半初始化对象

Happens-Before 不是"时间先后",而是"前一个操作的结果对后一个可见"的保证协议。判断并发安全不靠猜,靠「找两条操作,套 HB 规则能否连通」。

锁与 CAS 的选择

维度synchronized / ReentrantLock(悲观锁)CAS(乐观锁)
思路先加锁再操作,冲突一定防住先不改,更新时检查有没有被改过
代价线程阻塞/唤醒/上下文切换失败自旋,占 CPU
适合临界区复杂、多变量、竞争激烈、需等待唤醒简单原子操作、冲突不激烈、想免阻塞
代表转账、库存扣减+日志、生产者消费者自增计数、状态 0→1、引用替换

不变量:volatile 管「看得见最新值」,CAS 管「比较并更新这个动作是原子的」,两者各管一半、常搭配使用。synchronized 全包(可见性+原子性+有序性)。

synchronized 锁升级一句话

synchronized 的锁存在对象头 Mark Word 里,状态从低到高:无锁 → 偏向锁 → 轻量级锁 → 重量级锁只能升级不能降级。偏向锁(单线程反复加锁,仅 1 次 CAS 换 ThreadID)→ 轻量级锁(有竞争,CAS + 自旋,不阻塞)→ 重量级锁(自旋超限或多线程竞争,基于 OS Mutex,线程阻塞)。

线程池一句话

ThreadPoolExecutor 提交任务三步:① 线程数 < corePoolSize 直接建线程 → ② 否则入队 → ③ 队列满了且 < maximumPoolSize 再建线程,否则走拒绝策略。核心线程靠 queue.take() 阻塞保活,非核心线程靠 queue.poll(keepAliveTime) 超时回收。

常见误解(避坑)

  1. ❌ "volatile 能保证 count++ 原子"。→ volatile 只保证可见性,count++ 是「读-改-写」复合操作,仍会丢更新;要原子得用 CAS(AtomicInteger)或加锁。
  2. ❌ "锁越多越安全"。→ 锁粒度太小反而引发死锁;「粗粒度锁保护多资源」和「锁粒度太小」是死锁两大来源。
  3. ❌ "Executor 结尾的类都是线程池"。→ SimpleAsyncTaskExecutor 每次新开线程、不是池;Executor 接口只是"将来执行命令"的接口,真正代表线程池的是 ThreadPoolExecutor
  4. ❌ "线程池默认配置就够"。→ 必须手动 new ThreadPoolExecutorExecutors.newFixedThreadPool/newCachedThreadPool 用无界队列/无限线程,OOM 风险),且 workQueue 用有界队列。

关联

最近更新