Skip to content

并发编程 速查卡

🎯 覆盖 34 题 | ⭐ 高频 15 题 | 预计扫描 12 分钟 📌 先看⭐一句话答案 → 展开要点 → 自测清单检验


一、线程基础

知识地图:进程(资源分配) → 线程(CPU调度) → 虚拟线程(JVM调度,M:N)

⭐ 线程 vs 进程

一句话: 进程是资源分配的基本单位(独立地址空间),线程是 CPU 调度的基本单位(共享进程堆/方法区,私有栈/PC)。

维度进程线程
内存独立地址空间共享堆/方法区,私有栈/PC
切换大(切页表,TLB全失效)小(不切地址空间)
崩溃不影响其他进程可能拖垮整个进程

死锁四条件: "互占不循"——互斥、占有且等待、不可剥夺、循环等待

破坏方法: 固定加锁顺序(破循环) / tryLock 超时(破占有等待) / 避免嵌套锁


二、线程池

⭐ ThreadPoolExecutor 七参数

一句话: 核心参数——corePoolSize(核心线程) + maximumPoolSize(最大线程) + keepAliveTime(空闲存活) + workQueue(任务队列) + threadFactory(线程工厂) + handler(拒绝策略)。

⭐ 线程池执行流程

提交任务 → 线程数 < core? → 是: 创建核心线程执行
                           → 否: 队列未满? → 是: 入队等待
                                           → 否: 线程数 < max? → 是: 创建非核心线程
                                                                → 否: 执行拒绝策略

⚠️ 易错:核心线程不是 new ThreadPoolExecutor 时创建,而是第一次提交任务时按需创建

⭐ 四种拒绝策略

策略行为适用
AbortPolicy(默认)抛 RejectedExecutionException任务不可丢
CallerRunsPolicy调用者线程自己执行(自然限流)不想丢任务
DiscardPolicy静默丢弃允许丢(如日志)
DiscardOldestPolicy丢弃队列最老任务保留最新

⚠️ 易错:不要用 Executors.newFixedThreadPool(),它用无界队列(Integer.MAX_VALUE),会 OOM

线程数设置: CPU 密集型 = N+1;IO 密集型 = 2N(精确公式 = N/(1-阻塞系数))


三、锁与同步

知识地图:无锁 → 偏向锁 → 轻量级锁(CAS自旋) → 重量级锁(OS Mutex) [单向升级]

⭐ synchronized 原理 + 锁升级

一句话: synchronized 基于 Monitor 对象监视器实现,JDK 6 引入锁升级(偏向锁→轻量级锁→重量级锁)优化性能,锁状态存在对象头 Mark Word 中。

无锁 → 偏向锁(记录线程ID,同线程再次获取无需CAS)
     → 轻量级锁(CAS自旋,栈帧Lock Record写入Mark Word)
     → 重量级锁(ObjectMonitor,_EntryList阻塞+_WaitSet等待)

可重入: ObjectMonitor 的 _count 计数器,同线程获锁 _count++,释放 _count--,=0 时真正释放


⭐ volatile

一句话: volatile 保证可见性(写立即刷主存,读从主存取)和有序性(禁止重排序),但不保证原子性(i++ 不安全)。

经典应用——DCL 单例: 必须加 volatile 防止 new 操作的指令重排(分配内存→初始化→赋值引用,2和3可能重排)

维度volatilesynchronized
可见性
有序性
原子性
互斥
性能轻量(内存屏障)重(可能阻塞)

⭐ synchronized vs ReentrantLock

一句话: 大多数场景用 synchronized(简洁+JVM自动管理),需要超时获锁/可中断/公平锁/多条件变量时才用 ReentrantLock。

维度synchronizedReentrantLock
锁释放自动(退出同步块)手动(必须finally unlock)
可中断✅ lockInterruptibly()
超时✅ tryLock(timeout)
公平锁✅ new ReentrantLock(true)
条件变量1个(wait/notify)多个(newCondition)
锁升级❌(直接CAS+AQS)

⭐ AQS 原理

一句话: AQS 是 Java 并发包的骨架(ReentrantLock/Semaphore/CountDownLatch 都基于它),核心 = volatile int state(资源状态) + CLH 双向队列(等待线程)。

独占锁流程:tryAcquire(CAS改state) → 失败入CLH队列 → 前驱是Head时再尝试 → 成功出队
释放锁:tryRelease(state归0) → unpark 队列下一个线程

公平 vs 非公平: 非公平锁新来线程直接 CAS 抢占(不检查队列),吞吐量更高;公平锁先 hasQueuedPredecessors() 检查


四、ThreadLocal

⭐ ThreadLocal 原理 + 内存泄漏

一句话: 每个 Thread 持有一个 ThreadLocalMap(key=ThreadLocal 弱引用,value=强引用),线程隔离;使用完必须 remove() 防止线程池场景内存泄漏。

泄漏原因: ThreadLocal 被 GC 后 key=null,但 value 仍被 ThreadLocalMap 强引用 → 线程池中线程长期存活 → value 无法回收

解决: finally &#123; threadLocal.remove(); &#125; 必须执行

⚠️ 易错:key 用弱引用是"尽力而为"设计(至少 ThreadLocal 对象本身能被 GC),不代表不需要 remove

↳ 追问"子线程获取父线程值":InheritableThreadLocal(仅 new Thread 时复制) → TransmittableThreadLocal(线程池场景,任务提交时捕获快照)


五、虚拟线程

⭐ 虚拟线程 + 是否取代线程池

一句话: 虚拟线程(JDK 21)由 JVM 调度,创建成本极低(~1KB),IO 阻塞时自动卸载 carrier thread;用 per-task 模型替代传统线程池。

维度平台线程虚拟线程
创建成本~1MB 栈~1KB
调度OS 内核JVM 用户态(ForkJoinPool)
数量几千百万级
适合CPU 密集型IO 密集型

三个注意: ① ThreadLocal 内存膨胀(百万线程×副本)→用 ScopedValue ② synchronized 会 pin carrier thread→改用 ReentrantLock ③ CPU 密集型无优势

Spring Boot 3.2+: spring.threads.virtual.enabled=true 一行配置,Tomcat 自动用虚拟线程


补充速览

关键词核心答案
happens-beforeA hb B = A 的结果对 B 可见;八条规则:程序顺序/锁/volatile/线程启动终止/中断/终结/传递
CASCompare And Swap,CPU 原子指令(cmpxchg);三问题:ABA(加版本号)/自旋开销/只保证单变量
LongAdder分段CAS(base+Cell数组),高并发累加比 AtomicLong 快;sum()非原子
乐观锁 vs 悲观锁乐观(CAS/版本号,读多写少) vs 悲观(synchronized/lock,写多冲突频繁)
锁分类总览可重入/公平/独占/共享/自旋/分段/偏向+轻量+重量
JMM线程工作内存(CPU缓存) ↔ 主内存;volatile/synchronized 保证同步
结构化并发StructuredTaskScope(JDK21预览):子任务生命周期跟随父任务,自动取消
线程创建方式Thread/Runnable/Callable+FutureTask/线程池;本质都是 Thread.start()
⭐ CountDownLatch一个线程等N个线程完成(计数减到0);一次性不可重置;底层AQS共享锁
⭐ CyclicBarrierN个线程互相等待(到齐后同时继续);可重用;典型:多阶段并行计算
⭐ Semaphore控制并发数(限流);acquire获取/release释放许可;典型:连接池限流
⭐ CompletableFuture非阻塞链式异步:supplyAsync→thenApply→thenCombine/allOf/anyOf;指定业务线程池
⭐ ForkJoinPool分治+工作窃取(自己LIFO取,偷别人FIFO);parallelStream底层用commonPool

🧠 助记汇总

口诀含义
互占不循死锁四条件:互斥/占有等待/不可剥夺/循环等待
偏轻重(单向升级)synchronized 锁升级:偏向锁→轻量级锁→重量级锁
core→queue→max→reject线程池执行流程四步
弃拒丢旧四种拒绝策略:AbortPolicy/CallerRuns/Discard/DiscardOldest
state + CLHAQS 两大核心:volatile state + CLH 等待队列
用完必removeThreadLocal 防泄漏铁律

✅ 自测清单

#问题你能说出...
1线程 vs 进程核心区别 + 切换开销差异原因
2线程池七参数每个参数含义
3线程池执行流程四步判断链
4拒绝策略四种 + 各自适用场景
5synchronized 锁升级偏向→轻量→重量 + 各阶段原理
6volatile保证什么 + 不保证什么 + DCL 为什么需要
7sync vs ReentrantLock6 个核心区别
8AQSstate + CLH 队列 + 独占锁流程
9ThreadLocal 泄漏弱引用key+强引用value + remove
10虚拟线程与平台线程区别 + 三个注意事项
11死锁四条件 + 排查(jstack) + 解决
12并发工具三件套CountDownLatch/CyclicBarrier/Semaphore 各自等待模型和场景
13CompletableFutureallOf+join 并行查询 + 异常处理(exceptionally/handle)
14ForkJoinPool工作窃取算法 + parallelStream 为什么不适合 IO
12CAS原理 + 三个问题

💡 首次全部过一遍 → 第2天只过答不上来的 → 第4天再复习 → 面试前一天最后扫一遍