Java 并发常见问题总结(高频面试八股 + 真实生产问题全集)
这是一篇系统性、面试+生产双导向的 Java 并发问题总结,几乎覆盖了 90% 的并发面试题和线上事故根源。建议结合之前的基础篇(并发三性、JMM、Happens-Before、线程生命周期)一起复习。
一、基础概念类问题
- 并发与并行的区别?
- 并发:同一时间段内多个任务交替执行(单核也能并发)。
- 并行:同一时刻多个任务同时执行(依赖多核CPU)。
- 并发编程的三大核心问题是什么?
- 原子性、可见性、有序性。
- Java内存模型(JMM)解决了什么问题?
- 屏蔽各种硬件和操作系统的内存访问差异,保证并发程序在不同平台下能得到一致的执行结果。
- Happens-Before 原则有哪些?(至少能说出前6条)
- 程序次序规则、监视器锁规则、volatile变量规则、传递性、线程启动规则、线程终止规则、线程中断规则、对象终结规则。
二、线程相关高频问题
- start() 和 run() 的区别?
start():启动新线程,进入RUNNABLE状态,由JVM调用run()。run():普通方法调用,在当前线程执行,无新线程创建。
- 线程有哪几种状态?状态是如何转换的?
- NEW → RUNNABLE → BLOCKED / WAITING / TIMED_WAITING → TERMINATED。
- 线程的创建方式有哪些?哪种最好?
- 继承Thread、实现Runnable、实现Callable+FutureTask、线程池(推荐)。
- 守护线程和用户线程的区别?
- 守护线程:为主线程服务,JVM退出时自动结束(setDaemon(true)必须在start前)。
- 用户线程:普通线程,JVM退出需等待所有用户线程结束。
三、线程安全与锁机制问题
- 什么情况下会出现线程不安全?
- 多个线程同时读写共享变量,且至少有一个写操作,且没有正确同步。
- synchronized 的底层原理?
- 基于对象头中的Mark Word实现锁。
- 锁升级过程:偏向锁 → 轻量级锁(自旋) → 重量级锁(Monitor)。
- synchronized 和 ReentrantLock 的区别?(超级高频)
| 维度 | synchronized | ReentrantLock |
|---|---|---|
| 实现方式 | JVM内置(Monitor) | JDK API(AQS) |
| 可重入 | 是 | 是 |
| 公平锁 | 不支持 | 支持(可传入true) |
| 锁获取方式 | 自动获取/释放 | 手动 lock() + unlock()(需try-finally) |
| 等待可中断 | 不支持 | 支持(lockInterruptibly) |
| 超时获取锁 | 不支持 | 支持(tryLock(timeout)) |
| 条件等待 | 仅支持一个Condition | 支持多个Condition |
| 性能 | JDK1.6后优化后接近 | 更灵活,但代码稍复杂 |
- volatile 的作用?能保证原子性吗?
- 保证可见性 + 有序性(禁止指令重排序)。
- 不能保证原子性(i++ 仍非原子)。
- CAS 的原理?有什么缺点?
- Compare And Swap(乐观锁)。
- ABA 问题、无限循环(自旋开销)、只能保证单个变量原子性。
四、生产中最常见的并发问题
- 死锁(经典事故)
- 四个必要条件:互斥、请求并持有、不可剥夺、循环等待。
- 如何避免:固定加锁顺序、tryLock超时、资源一次性分配。
- 如何排查:jstack、jconsole、arthas thread -b。
- 活锁
- 线程不断尝试但始终无法推进(例如两个线程互相谦让)。
- 线程饥饿
- 低优先级线程长期得不到CPU时间。
- 上下文切换开销过大
- 线程数远大于CPU核数时,频繁切换导致性能急剧下降。
- 经验:CPU密集型线程数 ≈ CPU核数;IO密集型 ≈ CPU核数 × 2~4。
- ThreadLocal 内存泄漏
- 原因:ThreadLocalMap 的 key 是弱引用,value 是强引用。
- 解决:使用完后手动 remove(),或在线程池中特别注意。
- 线程池参数设置不当导致的问题
- 核心线程数、最大线程数、队列容量、拒绝策略设置错误。
- 常见事故:队列用无界LinkedBlockingQueue,导致OOM。
- 并发容器使用错误
- 迭代时修改(ConcurrentModificationException)。
- HashMap 在高并发下死循环(JDK7扩容时)。
- 应该使用 ConcurrentHashMap、CopyOnWriteArrayList 等。
五、并发工具类问题
- CountDownLatch vs CyclicBarrier vs Semaphore
- CountDownLatch:一次性倒计时(不可重用)。
- CyclicBarrier:可重用栅栏,所有线程到达后再一起通过。
- Semaphore:限流(控制同时访问资源的线程数)。
- Future / CompletableFuture 的使用场景?
- AQS(AbstractQueuedSynchronizer)是什么?
- ReentrantLock、Semaphore、CountDownLatch 等工具类的底层框架。
- 核心是 state + CLH 等待队列。
六、生产调优与排查经验
- 线上CPU 100%:arthas thread、jstack 找热点线程。
- 频繁 Full GC:ThreadLocal 未 remove、大量对象创建在线程池中。
- 响应时间突增:锁竞争严重、线程池队列积压。
- 死锁/活锁:arthas dashboard + thread -b。
七、并发问题总结口诀(便于背诵)
三性不保出事故,死锁活锁饥饿堵;
volatile见序不原子,synchronized锁升级;
ReentrantLock更灵活,AQS统一做底层;
线程池参数要慎设,ThreadLocal记得remove;
并发容器用Concurrent,迭代修改要小心。
推荐复习顺序:
- 并发基础(三性、JMM、Happens-Before)
- 线程与线程池
- synchronized + volatile + CAS
- AQS + ReentrantLock
- 并发工具类(JUC)
- 并发容器 + ThreadLocal
- 生产问题排查(arthas/jstack)
想深入哪一块?我可以立刻给你展开完整一篇:
- 「synchronized 锁升级全过程 + 源码」
- 「AQS 原理与 ReentrantLock 实现」
- 「线程池 7 大参数 + 源码执行流程」
- 「CompletableFuture 异步编程实战」
- 「生产中遇到的 10 个经典并发事故复盘」
直接回复对应标题或“下一节 AQS”,我马上给你最详细的版本。
并发是Java进阶的分水岭,学透了这些,你就真正具备了写高并发系统的能力!继续加油!