Java 并发编程核心

写 Java 代码写过几年的人,都会被并发问题折磨过。

我带过的学员里,有个阿里 P7 候选人技术面全过,结果挂在了一道"ThreadLocal 内存泄漏"的追问上——他用了 ThreadLocal 做线程隔离,但在线程池场景下忘记在 finally 里 remove(),导致大量数据泄漏到其他请求里。

并发编程是 Java 里公认最难掌握的领域之一:JMM 决定了可见性和有序性,synchronized 和 JUC 锁管理了原子性,线程池管理了资源分配——每一块都足够写一本书。

这套内容,就是帮你把并发从"会用"提升到"真懂"。

内容总览

Java 并发编程可以拆成四个层次:

层次核心问题涉及知识点
模型层线程之间怎么通信、怎么保证顺序JMM、happens-before、volatile
锁层怎么保护共享资源的原子性synchronized、ReentrantLock、CAS、AQS
工具层并发场景有哪些常用工具CountDownLatch、CyclicBarrier、Semaphore、ThreadLocal
资源层怎么管理线程生命周期线程池、阻塞队列、拒绝策略

知识全景

模型层:理解 JMM 🔴🔴

Java 内存模型(JMM)是整个并发的基础。不理解 JMM,就永远说不清楚"为什么 volatile 可见"、"为什么需要 happens-before"。

Java内存模型(JMM)

JMM — 为什么 CPU 缓存和编译器优化会导致可见性和有序性问题

happens-before 原则 — 8 条规则,理解这些规则才能写对多线程代码

volatile 可见性与禁止重排序 — 两条核心保证:可见性 + 有序性

JMM 的核心思想:在多线程环境下,编译器/CPU 的优化不能破坏 happens-before 规则的顺序保证。通过 happens-before 规则,JMM 给程序员提供了编写正确并发代码的 contract。

💡

volatile 不是万能的。它能保证可见性和有序性,但不能保证原子性(比如 i++ 这种读-改-写操作)。volatile 不等于"线程安全"。

锁层:从 synchronized 到 AQS 🔴🔴

synchronized 原理与锁升级

synchronized 对象头(Mark Word)

synchronized 是 Java 最基础的锁。JDK6 之后引入了锁升级机制:偏向锁 → 轻量级锁 → 重量级锁。这个设计是为了减少无竞争场景下的性能开销。

synchronized vs ReentrantLock — 什么时候选 synchronized,什么时候选 ReentrantLock

CAS 原理与 ABA 问题 — 无锁并发的基础,理解它才能理解 ConcurrentHashMap 的无锁设计

AQS 抽象队列同步器原理 — JUC 并发包的基石,ReentrantLock、Semaphore、CountDownLatch 都基于它

ReentrantLock 公平锁 vs 非公平锁 — 公平 vs 非公平的 trade-off

ReentrantReadWriteLock 读写锁 — 读多写少场景的性能优化

Condition 条件队列 — await/signal 的原理

⚠️

synchronized 的锁升级是不可逆的——偏向锁可以升级为轻量级锁,轻量级锁可以升级为重量级锁,但反过来不行。JDK15 之后偏向锁被废弃了,因为现代 JVM 的偏向锁优化收益越来越小。

工具层:常用并发工具 🟡

CountDownLatch 原理与使用 — 一次性屏障,等 N 个任务完成

CyclicBarrier 原理与使用 — 可重复使用的屏障,等 N 个线程都到达某点

Semaphore 信号量 — 控制同时访问资源的线程数

ThreadLocal 原理与内存泄漏 — 线程封闭的核心工具,但有内存泄漏风险

⚠️

ThreadLocal 的内存泄漏是生产中的高频问题。ThreadLocalMap 的 key(ThreadLocal)是弱引用,但 value 是强引用。在线程池场景下,线程复用但 ThreadLocalMap 不会清空,value 不会被 GC。如果 ThreadLocal 不再被外部引用(key 变 null),value 就会泄漏。必须在线程使用完 ThreadLocal 后调用 remove()。

资源层:线程池 🔴

线程池是 Java 并发中最需要掌握的工程工具。不会配置线程池,并发程序要么资源耗尽,要么性能极差。

线程池7个核心参数 — corePoolSize/maximumPoolSize/keepAliveTime/workQueue/threadFactory/handler

线程池 execute 流程 — 判断流程:核心线程 → 队列 → 最大线程 → 拒绝

线程池拒绝策略对比 — AbortPolicy/CallerRunsPolicy/DiscardPolicy/DiscardOldestPolicy

线程池大小如何设置 — CPU 密集型 vs IO 密集型怎么算线程数

阻塞队列(Array/Linked/Synchronous/Delay) — 队列类型对线程池行为的影响

高级并发:CompletableFuture 与 ForkJoin 🟡

CompletableFuture 异步编程 — 链式异步编程,代替回调地狱

Fork/Join 框架 — 分治并行框架,用于递归计算任务

基础概念 🟡

进程 vs 线程 vs 协程 — 理解三者的本质区别

面试关联

并发编程和其他 Java 模块紧密相连:

阅读建议

你的阶段推荐顺序
面试冲刺JMM → volatile → synchronized → CAS → AQS → 线程池
系统学习进程/线程 → JMM → happens-before → volatile → synchronized → 锁升级 → CAS → AQS → JUC 工具 → 线程池
查漏补缺直接找对应知识点定向阅读

记住:并发编程没有捷径。每一个工具、每一个概念,都要理解"它解决了什么问题"、"它本身会带来什么问题"。只知道怎么用,不知道原理和陷阱,是并发编程最大的坑。