901 字
约 3 分钟
3
S05-B02 · 线程状态机与创建方式
适用读者:分不清
BLOCKED与WAITING的人。
前置:S05-B01。
口径:本系列唯一无旧卡的一篇(原卡 A-1610 归入 S04),内容为 E 驱动;jstack观察为实测。
覆盖旧文:—(E 驱动)
覆盖扩展:E-S05-003(六状态与 BLOCKED 区别)。
上一篇:S05-B01 · 并发三大特性与 JMM
下一篇:S05-B03 · synchronized 与锁升级。
1. 一句话结论
Java 线程有 6 种状态,判断"卡在哪"只需看两件事:占用 CPU 吗?在等锁还是在等条件?
| 状态 | 在等什么 | 占 CPU |
|---|---|---|
NEW |
还没 start() |
— |
RUNNABLE |
CPU 时间片(含 IO 等待) | 可能 |
BLOCKED |
等 monitor 锁 | ❌ |
WAITING |
等 notify/join(无超时) |
❌ |
TIMED_WAITING |
等超时(sleep/wait(n)) |
❌ |
TERMINATED |
已结束 | — |
关键区别:BLOCKED 专指"等 synchronized 锁";WAITING 是"等别人显式唤醒"。这是面试高频点,也是排障时区分"锁竞争"与"逻辑等待"的依据。
2. start() 与 run():本质不同
| 调用 | 行为 |
|---|---|
t.start() |
创建新线程,新线程执行 run() |
t.run() |
当前线程直接调用方法(没有并发) |
判据:"我是在开新线程,还是在调一个普通方法?" 直接调 run 是最常见的"假多线程"bug。
3. 三种创建方式与取舍
| 方式 | 写法 | 取舍 |
|---|---|---|
继承 Thread |
class T extends Thread |
占用了继承名额,不推荐 |
实现 Runnable |
new Thread(r) |
推荐(可再实现其他接口) |
Callable + Future |
有返回值、可抛异常 | 需要结果时用 |
| 线程池 | ExecutorService.submit |
生产环境唯一推荐 |
为什么生产环境不用 new Thread:线程创建/销毁成本高、无上限、无法统一监控——这些正是 B05 线程池要解决的。
4. 用 jstack 看状态(实测方法)
$ jstack <pid> | grep -A 3 "java.lang.Thread.State"
典型输出含义:
| 输出 | 解读 |
|---|---|
RUNNABLE + 栈在 socketRead |
实际在等 IO(Java 里网络 IO 也显示 RUNNABLE) |
BLOCKED (on object monitor) |
等锁 → 查是谁长期持有锁 |
WAITING (parking) |
等 LockSupport.park(AQS 常见) |
TIMED_WAITING (sleeping) |
在 sleep |
注意:RUNNABLE 不等于"在跑"——Java 把"等 IO"也算 RUNNABLE,所以看 CPU 占用要配合 top -H。
5. 五个误区
| 误区 | 真相 |
|---|---|
"run() 会开新线程" |
在当前线程执行 |
"WAITING 和 BLOCKED 一样" |
前者等唤醒,后者等锁 |
"RUNNABLE 就是在用 CPU" |
等 IO 也是 RUNNABLE |
"sleep(0) 没意义" |
会让出 CPU(让同优先级线程有机会) |
| "线程越多并发越高" | 受核数与切换成本限制(S02-B02 实测) |
6. 自检
- 六种状态分别"在等什么"?哪两个都不占 CPU 却含义不同?
start()与run()的本质差异?- 为什么生产环境不直接
new Thread? jstack里RUNNABLE但 CPU 不高,可能是什么情况?WAITING与TIMED_WAITING的区别?
7. 下一篇
S05-B03 · synchronized 与锁升级
状态机讲完,下一篇深入"锁":锁的是什么、三级升级的触发条件,以及为什么偏转锁在 JDK 15+ 被废弃。
本篇属于系列《S05 · Java 并发与 JVM》。本篇为 E 驱动(原卡 A-1610 归 S04);扩展点 E-S05-003。
S05-B02 · 线程状态机与创建方式
https://www.clxhxhhr.top/posts/5243/ 评论
0 条
还没有评论,先写一条吧。