JJava 知识库
JAVA INTERVIEW

高频面试题

Java多线程基础约 3 分钟

线上接口卡住但 CPU 不高,线程快照里大量 WAITING 和 BLOCKED,你会怎么分析?

参考回答约 3 分钟 · 口语表达
先说结论

我会先控制影响,再按证据定位:Java 线程有 NEW、RUNNABLE、BLOCKED、WAITING、TIMEDWAITING 和 TERMINATED 六种状态。线程协作可以使用监视器的 wait/notify、Lock 的 Condition、join、中断标记以及并发工具类;生产代码通常优先高层并发组件。

01

我会先确认影响范围,同时控制故障继续放大。理解线程状态转换以及 wait、notify、join 和中断的协作方式。Java 线程有 NEW、RUNNABLE、BLOCKED、WAITING、TIMEDWAITING 和 TERMINATED 六种状态。线程协作可以使用监视器的 wait/notify、Lock 的 Condition、join、中断标记以及并发工具类;生产代码通常优先高层并发组件。 Java 的 RUNNABLE 同时包含操作系统层面的就绪和运行。

02

止损之后,我会按请求链路建立证据,而不是凭经验猜。沿着「创建线程对象 → start 进入 RUNNABLE → 竞争锁或等待条件 → 被通知后重新竞争 → 完成任务进入 TERMINATED」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 创建线程对象 NEW 线程尚未启动,同一 Thread 实例只能成功 start 一次。 start 进入 RUNNABLE Java 的 RUNNABLE 同时涵盖操作系统可运行与正在运行,不能仅据此判断是否占用 CPU。 java.lang.Thread.State:六种 JVM 线程状态。 ObjectMonitor/LockSupport:wait/notify 与 park/unpark 差异。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。

03

定位时我最关注这些参数、指标和容量关系。启动两个消费者和一个生产者,制造伪唤醒/竞争;用 jstack 区分 BLOCKED、WAITING 和 TIMEDWAITING。

04

找到根因后先做最小修复,再用同样的流量验证。代码用 if 判断队列为空后 wait,被唤醒时未重新检查条件,多个消费者竞争后其中一个拿走任务,另一个继续执行并失败。改为 while 条件循环,并在同一锁下修改队列和发送通知后才正确。 用 if 代替 while 等待条件:各线程状态数量:if wait 改 while。 持锁 sleep 导致其他线程无法推进:等待持续时间:通知与状态修改放同一锁。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「等待线程数」为主基线,记录值应满足「与空闲消费者一致」;同时保存 各线程状态数量、等待持续时间,使后续变化能够回到同一时间轴比较。

05

恢复阶段要逐步放量,最后把监控和边界补齐。方案:更适合的场景:主要收益:代价与边界。 wait/notify:同一 monitor 下的底层条件协作:无需额外类:易丢通知,单条件表达困难。 Condition:显式锁且有多个等待条件:条件队列清晰、支持超时中断:必须严格持锁调用。 BlockingQueue/同步器:标准生产消费或阶段协作:高层语义、安全成熟:需要选择容量与一致性语义。

排查与恢复时间线从目标到落地
01创建线程对象
02start 进入 RUNNABLE
03竞争锁或等待条件
04被通知后重新竞争
05完成任务进入 TERMINATED