【Java 并发-面试】从线程基础到企业级开发的知识点概况

文章目录

先以电商秒杀场景串联多线程全知识点:

  1. 生命周期:秒杀请求触发Tomcat线程池创建工作线程(新建)→ 争抢库存锁(就绪)→ 执行业务逻辑(运行)→ 等待Redis响应(阻塞)→ 完成扣减(终止);
  2. 数据存储:ThreadLocal存储用户秒杀上下文,共享库存变量用AtomicInteger保证原子性;
  3. 线程管理:自定义线程池处理异步下单,配置核心/最大线程数+队列,设置CallerRunsPolicy拒绝策略;
  4. 线程协作:用Semaphore限流、CountDownLatch汇总多线程库存校验结果;
  5. 问题与排查:锁竞争导致线程阻塞、线程池耗尽引发请求堆积,通过jstack分析线程状态、jconsole监控线程池指标定位问题。

一、从基础到高层:多并发的架构视角

多线程思考基本框架:

  1. 线程是如何跑的,
    多并发是在回答一层层设计问题:谁在执行、谁在共享、谁在加载、谁先谁后、如何协作、如何管好资源与可观测性。下面按「从基础到高层」串一条线,便于建立体系。

第一层:执行单元------线程是什么、谁在跑

要回答的问题:请求/任务最终是在什么「执行单元」上跑的?这些单元如何创建、如何调度、有哪些状态?

  • 线程是进程内的执行单元 ,是操作系统调度的最小单位;在 Java 里,一个线程对应一条执行流,有独立的栈和程序计数器,共享堆和方法区
  • 架构上要建立的第一点认知:线程是资源单位 。系统能同时处理多少请求、延迟多高,很大程度上由「有多少线程、这些线程在干什么」决定。所以做高并发设计时,先要画出线程模型(谁 accept、谁 IO、谁算业务、谁回调),再谈锁和队列。

第二层:共享与互斥------多线程下数据为何会错、如何保护

要回答的问题:多个线程同时访问同一份数据时,为什么会错?语言和 JVM 提供了哪些手段保证「正确性」?

  • 并发不安全的本质往往可归结为三件事:可见性 (一个线程改完另一个看不到)、原子性 (多步操作被交错)、有序性 (指令重排导致与预期不符)。解决手段从语言层看主要是: (synchronized、ReentrantLock)和 volatile(配合下一层 JMM 理解)。
  • 架构上要建立的第二点认知:锁的粒度与一致性边界。锁太大,串行化严重;锁太小,易出错。单机锁只保证单 JVM 内一致,跨机一致要依赖事务、消息、分布式锁等;架构师要明确「一致性边界画在哪一层」。

第三层:类与线程的边界------谁加载类、多租户与插件化

要回答的问题:类是由谁加载的?多应用、多租户、插件化场景下,如何做到「类隔离」或「父调子」?

  • JVM 通过 ClassLoader 加载类,双亲委派保证了类在 JVM 内的唯一性与安全;线程上下文类加载器打破了「父不能直接用子加载的类」的限制,是 SPI、JDBC Driver、插件化的常用手段。
  • 架构上要建立的第三点认知:类加载边界与线程绑定 。中间件、多租户、热部署都会动到 ClassLoader;要清楚「谁设置、谁使用」以及生命周期,避免类找不到或内存泄漏。这一层和「线程」的关系在于:很多加载动作发生在线程执行过程中,且上下文类加载器是每线程一份的。

第四层:内存可见性与有序性------JMM 与 volatile

要回答的问题:多线程下「谁先执行、谁对谁可见」由什么规则保证?volatile 能解决什么、不能解决什么?

  • JMM(Java 内存模型) 规定了主内存与工作内存的交互、happens-before 等规则,是理解可见性、有序性的基础。volatile 保证可见性与禁止指令重排,但不保证复合操作的原子性;适合做状态标志、DCL 单例的实例引用等。
  • 架构上要建立的第四点认知:语言层一致性 ≠ 分布式一致性。JMM 解决的是「同一 JVM 内多线程」的可见与有序;跨进程、跨机器要依赖分布式事务、消息、版本号等。在做缓存、本地队列、多级存储时,要明确「谁可见、谁先发生」,避免想当然。

第五层:协作与模式------线程间如何配合、JUC 与设计模式

要回答的问题:除了「一个线程一把锁」之外,线程之间如何协作?有哪些可复用的结构(模式)?

  • wait/notifyJUC (Lock、原子类、队列、CountDownLatch、Semaphore 等),再到 设计模式(Future、Guarded Suspension、ThreadLocal、Latch、Worker-Thread、Active Objects、Event Bus),都是在回答「如何协调多条执行流」。
  • 架构上要建立的第五点认知:模式是结构的复用。Worker-Thread 与线程池本质都是「有限 worker + 任务队列」,区别在 API 与生命周期;Event Bus 与 MQ 的边界在进程内/跨进程、持久化与可靠性。选型时看的是「扩展点」和「约束」,而不是死记名字。
  • 这一层要掌握:JUC 常用工具、AQS 的抽象、Future/CompletableFuture 的编排、ThreadLocal 的原理与内存泄漏注意点、常见多线程设计模式的适用场景。目的是:在做异步、多路调用、事件驱动、上下文传递时,能选对工具和模式,并说清与 MQ、线程池的边界。

第六层:资源与可观测性------线程池、异步结果、问题归因

要回答的问题:线程从哪里来、到哪里去?如何控制并发度与资源占用?出问题时如何快速归因?

  • 线程池 把「创建/销毁线程」变成「复用 + 排队」,是控制并发度与资源的上界;Future/CompletableFuture 解决「异步结果怎么拿、怎么编排、怎么超时」。Thread Dump 和线程池监控解决「卡顿、死锁、CPU 高、线程打满」时的归因。
  • 架构上要建立的第六点认知:线程是资源、可观测性是能力。线程池参数(核心/最大/队列/拒绝)决定了系统能扛多少并发、过载时如何降级;线程命名、Dump、队列与活跃数监控决定了能否快速定位问题。容量规划、限流、熔断、优雅停机都建立在这一层之上。
  • 这一层要掌握:线程池的组成与执行顺序、ThreadPoolExecutor 参数与拒绝策略、Future/CompletableFuture 的用法、jstack 与 Thread Dump 的分析思路。目的是:在设计阶段定好线程池与拒绝策略,在运维阶段能结合 Dump 与监控快速归因,而不是凭感觉或事后救火。

小结:一条线串起来

  • 基础:线程是执行单元、是资源单位 → 先有「谁在跑」的模型。
  • 安全:共享与互斥、锁与死锁 → 再谈「谁在共享、如何不错」。
  • 边界:ClassLoader 与线程上下文类加载器 → 明确「谁加载类、类隔离与跨层加载」。
  • 可见与有序:JMM 与 volatile → 明确「谁先谁后、谁对谁可见」。
  • 协作:JUC 与设计模式 → 明确「如何协调多条执行流、如何选模式」。
  • 资源与可观测:线程池、Future、Dump → 明确「如何管好资源、如何排查」。

按这条线建立体系,多并发的知识就不再是零散的「六块」,而是从执行单元 → 共享与互斥 → 类边界 → 内存模型 → 协作与模式 → 资源与可观测的递进;架构师在做方案、做评审、做排查时,可以按层对号入座,知道「这一层要回答什么问题、用到哪些知识点」。


二、线程:如何创建、生命周期与状态、常用 API

知识点概况

线程是进程内的执行单元,共享进程内存,有独立的程序计数器与栈。要掌握三件事:怎么创建、有哪些状态、常用 API 怎么用。

  • 创建方式:继承 Thread、实现 Runnable/Callable、通过线程池提交任务;核心是理解「任务」与「线程」分离(策略模式)。
  • 生命周期与状态:NEW → RUNNABLE(就绪/运行)→ BLOCKED / WAITING / TIMED_WAITING → TERMINATED;要知道每个状态因何进入、如何退出。
  • 常用 API:start()run() 的区别;sleepyieldjoininterrupt 对状态的影响;线程命名、优先级、守护线程、ThreadGroup、上下文类加载器。
  • 异常与 Hook:UncaughtExceptionHandler 捕获线程内未处理异常;Runtime.addShutdownHook 在 JVM 退出前执行逻辑。

在实际业务开发与架构设计中的注意点

  • 线程是资源单位而非业务概念,系统吞吐与延时由「线程模型」决定(如 N 个 accept 线程 + M 个 worker 线程)。
  • 统一线程命名,便于日志与 Dump 排查;控制线程数量,避免无界创建导致 OOM;明确守护线程只做辅助,不承载核心业务。
  • 做高并发方案时,先画出「请求进来到响应出去」的线程流转图,再谈锁与队列。

参考链接(① 线程的基础知识)


三、线程安全:为什么共享数据会错、synchronized 与死锁

知识点概况

多线程共享变量时出现数据不一致,本质往往可归结为三方面:可见性、原子性、有序性。解决手段以 synchronized 为主,同时要会排查死锁、会选锁。

  • 并发不安全的三个原因:可见性 (一线程改完另一线程看不到)、原子性 (多步操作被交错执行)、有序性(指令重排导致与预期不符)。
  • synchronized:Java 内置互斥手段;Monitor、方法锁与代码块锁、This Monitor 与 Class Monitor;锁升级(偏向锁、轻量锁、重量锁)、锁粗化与锁消除。
  • 死锁:成因(互斥、持有并等待、不可剥夺、环路等待);诊断用 jstack 等工具。
  • 锁的选用:内置锁(synchronized)、显式锁(ReentrantLock、ReadWriteLock)、乐观锁(CAS)在不同场景下的取舍。

在实际业务开发与架构设计中的注意点

  • 锁的粒度与性能直接影响并发能力:锁范围过大导致串行化、过小则易出错;高并发场景常通过缩小临界区、无锁结构(CAS、不可变)、读写分离降低锁竞争。
  • 死锁预防应在设计阶段考虑:统一加锁顺序、使用 tryLock 超时、避免在持锁时调用外部。
  • 微服务与分布式场景下,线程内锁只保证单机一致,跨机一致要依赖事务、消息、分布式锁等;方案设计时要明确「一致性边界」在哪一层。

参考链接(② 线程的数据如何存储)


四、线程间协作:wait/notify、阻塞与异步、Hook 与优雅停机

知识点概况

多个线程要协同工作,要么用 wait/notify 在单 JVM 内协作,要么用消息、事件做异步;同时要会管线程组、抓异常、做优雅停机。

  • 同步阻塞与异步非阻塞:区别在于是否占用线程等待;高并发下常选异步。
  • wait/notify:单机内多线程协作;条件不满足时等待(Guarded Suspension)或直接返回(Balking);可结合「条件变量 + 锁」理解(如手写 BooleanLock)。
  • ThreadGroup:线程的组织与批量操作。
  • 异常与 Hook:UncaughtExceptionHandler 捕获线程内异常;Runtime.addShutdownHook 在 JVM 退出前执行,用于优雅停机(关连接、刷缓冲等)。

在实际业务开发与架构设计中的注意点

  • 同步阻塞 vs 异步非阻塞是方案选型的核心:阻塞模型简单但占用线程,高并发下常改为 NIO + 事件循环或异步回调。
  • wait/notify 适合「单机、单 JVM」内的生产者-消费者;跨进程、跨服务则用消息队列、Event Bus。
  • Hook 线程用于优雅停机(关闭连接、flush 缓冲、上报状态);设计时要约定停机超时与强制退出策略,避免 Hook 内死锁或长时间阻塞。

五、线程池:复用与排队、参数与拒绝策略、异步结果 Future

知识点概况

线程池用「少量线程 + 任务队列」复用线程、控制并发;要会配参数与拒绝策略,会用 Future/CompletableFuture 拿异步结果。

  • 线程池的组成:任务队列、线程数量(核心/最大)、拒绝策略、线程工厂。
  • 执行顺序:先填满核心线程 → 再入队 → 队列满再扩到最大线程 → 再满则触发拒绝策略。
  • ThreadPoolExecutor:核心参数、常见队列(有界/无界)、拒绝策略(Abort、CallerRuns、Discard、DiscardOldest)。
  • 异步结果:Callable、Future、FutureTask 的用法;CompletableFuture 的链式编排、allOf/anyOf、异常处理。

在实际业务开发与架构设计中的注意点

  • 线程池是并发度与资源的上界,必须显式设定核心/最大线程数、队列容量与拒绝策略,避免无界队列导致 OOM 或静默堆积。
  • 容量规划:根据 QPS、单请求耗时估算所需线程数;拒绝策略:核心链路常用 CallerRuns 或降级,非核心可 Discard 并打点监控。
  • Future/CompletableFuture 用于任务编排与超时控制,在微服务中常与 HTTP 客户端、RPC 超时配合;方案设计时要统一「超时与熔断」策略。

参考链接(④ 线程的管理)


六、类加载与线程:双亲委派、谁加载类、线程上下文类加载器

知识点概况

类由谁加载、按什么顺序加载,会影响多线程下的类隔离与 SPI;线程上下文类加载器是打破双亲委派、实现「父调子」的常用手段。

  • 类的加载过程:加载、链接、初始化。
  • 双亲委派:类优先交给父加载器;保证类在 JVM 内的唯一性与安全。
  • JVM 三大类加载器:Bootstrap、Extension、Application。
  • 线程上下文类加载器:父加载器加载的代码可能依赖子加载器加载的实现(如 JDBC Driver);通过 Thread.currentThread().getContextClassLoader() 打破单向依赖,在 SPI、插件化中常用。

在实际业务开发与架构设计中的注意点

  • 双亲委派保证类在 JVM 内的唯一性与安全;中间件、框架若自定义类加载器,要清楚类隔离边界(如 Tomcat 多应用、多租户隔离)。
  • 线程上下文类加载器是跨层加载的桥梁;在做组件化、热部署、多版本共存时,要明确「谁设置、谁使用」以及生命周期,避免类找不到或内存泄漏。
  • 云原生与容器环境下,类加载还与镜像分层、启动速度相关。

参考链接


七、内存可见性与有序性:JMM、volatile、单例怎么写才安全

知识点概况

多线程下「谁先执行、谁对谁可见」由 JMM 规定;volatile 能保证可见与禁止重排,是写安全单例和状态标志的常用手段。

  • JMM(Java 内存模型):主内存与工作内存的抽象;happens-before 等规则是理解可见性、有序性的基础。
  • volatile:保证可见性、禁止指令重排,不保证原子性;适用场景如状态标志、DCL 单例中的实例引用。
  • 单例的多种写法:饿汉、懒汉、懒汉+同步、Double-Check、Volatile+DCL、Holder、枚举;从线程安全、懒加载、反序列化安全等维度对比选用。

在实际业务开发与架构设计中的注意点

  • JMM 是语言层的一致性模型,与分布式系统中的「一致性」有类比但不相同:多线程是共享内存,分布式是网络分区与延迟;做缓存、本地队列、多级存储设计时,要明确「谁可见、谁先发生」。
  • volatile 适合「一写多读」的状态位;复杂共享状态仍需要锁或原子类。
  • 单例在实际业务中常用于无状态服务、工具类、连接池入口;选型时要考虑序列化、反射、多 ClassLoader 环境下的唯一性。

参考链接(② 线程的数据如何存储)


八、JUC 与多线程设计模式:锁与原子类、AQS、Future/ThreadLocal/Event Bus

知识点概况

除了 synchronized/wait-notify,JUC 提供更丰富的锁、原子类、队列、Latch 等;AQS 是很多同步器的底座;设计模式把「任务与线程、异步与回调」抽象成可复用的结构。

  • JUC 常用工具:Lock、ReadWriteLock;原子类(AtomicXxx、LongAdder);阻塞队列;CountDownLatch、CyclicBarrier、Semaphore。
  • AQS(AbstractQueuedSynchronizer):state、CAS、CLH 队列;很多锁和同步器基于它实现。
  • 常见设计模式:Future(先拿凭据、后取结果);Guarded Suspension(条件等待);ThreadLocal(线程局部存储,注意内存泄漏);Balking;Latch(多任务完成后汇总);两阶段终止;Worker-Thread(Channel + 工作线程);Active Objects(异步接口 + 内部队列);Event Bus(订阅/发布);Event Driven 架构。

在实际业务开发与架构设计中的注意点

  • 模式是结构的复用,选型时看「扩展点」和「约束」:Worker-Thread 与线程池本质都是「有限 worker + 任务队列」,区别在 API 与生命周期;Active Objects 适合「异步接口、同步语义」的封装,便于与 RPC、消息对接。
  • Event Bus 用于解耦、异步、审计,要界定与 MQ 的边界(进程内 vs 跨进程、持久化与可靠性)。
  • ThreadLocal 在连接池、事务上下文、链路追踪中广泛使用;要约定设置与清理的边界(如 Filter/Interceptor 的 before/after),避免线程复用导致上下文串线。

参考链接(② ThreadLocal & ③ 线程协作)


九、线上线程问题:怎么抓 Dump、怎么看状态与锁、怎么分析死锁

知识点概况

线上卡顿、死锁、CPU 飙高、线程数过多时,需要抓线程快照并看懂「谁在等谁、谁持有什么锁」;jstack 是常用手段。

  • 抓取快照:用 jstack 获取 Thread Dump。
  • 线程状态在 dump 中的含义:RUNNABLE、BLOCKED、WAITING、TIMED_WAITING;锁与 monitor 信息、持有者与等待者。
  • 死锁:jstack 末尾会输出死锁检测结果。
  • 分析思路:先看状态分布,再针对 BLOCKED/WAITING 看锁的持有与等待关系,结合业务代码、锁使用与线程池配置做归因。

在实际业务开发与架构设计中的注意点

  • 可观测性是稳定运行的基础:线程模型要「可解释、可排查」------统一命名、关键路径打点、线程池与队列监控。
  • Thread Dump 是事后分析的重要手段;应约定规范:保留 Dump 的时机(如 CPU 高、RT 突增)、与链路/日志的关联。
  • 容量规划与限流可减少「线程打满、队列堆积」类问题;死锁则需在代码评审与测试阶段关注加锁顺序与超时。

参考链接(⑤ 企业级操作)


相关推荐
云中飞鸿2 小时前
VS2015安装后,安装QT59,之后安装qt-vsaddin-msvc2015-2.4.3.vsix 文件失败问题!
开发语言·qt
m0_748233172 小时前
C与C++:底层编程的六大核心共性
java·开发语言
沐知全栈开发2 小时前
HTTP Content-Type
开发语言
坊钰2 小时前
【Rabbit MQ】Rabbit MQ 介绍
java·rabbitmq
一切尽在,你来2 小时前
C++多线程教程-1.2.2 C++标准库并发组件的设计理念
开发语言·c++
雀啼春2 小时前
Java中的数据类型
java
80530单词突击赢2 小时前
C++关联容器深度解析:set/map全攻略
java·数据结构·算法
m0_561359672 小时前
代码热更新技术
开发语言·c++·算法