Java 6种线程状态 · 极简速记
一、6种状态核心定义(按生命周期排序)
| 状态 | 含义 | 触发场景 |
|---|---|---|
| NEW | 线程刚创建,还没调用 start() |
new Thread() 之后 |
| RUNNABLE | 可运行状态(包含就绪/运行中) | 调用 start() 后,或从阻塞/等待状态恢复 |
| BLOCKED | 锁竞争失败,阻塞等待 synchronized 锁 |
抢内置锁失败;wait() 被唤醒后抢锁失败 |
| WAITING | 无限期等待,需要别人主动唤醒 | wait() / join() / LockSupport.park() |
| TIMED_WAITING | 有限期等待,超时自动唤醒 | sleep() / wait(time) / join(time) / parkNanos() |
| TERMINATED | 线程执行完毕,生命周期结束 | run() 方法正常执行完 / 抛出异常终止 |
二、关键状态辨析
1. BLOCKED vs WAITING / TIMED_WAITING
| 维度 | BLOCKED | WAITING / TIMED_WAITING |
|---|---|---|
| 触发原因 | 抢 synchronized 锁失败 |
主动调用 wait() / sleep() / join() 等方法 |
| 是否释放锁 | 不释放锁(锁被别人拿着,自己排队) | 会释放锁(比如 wait() 会主动释放锁) |
| 唤醒方式 | 持有锁的线程释放锁后,自动抢锁 | 被 notify() / notifyAll() 唤醒,或超时唤醒 |
| 核心区别 | 被动阻塞,和锁强相关 | 主动等待,可由时间或其他线程唤醒 |
2. 为什么 sleep() 不会释放锁,而 wait() 会?
sleep():只是让线程睡一会儿,不涉及锁的操作,锁依然被持有wait():必须在synchronized代码块内调用,会主动释放锁,让其他线程有机会获取锁
三、完整状态流转图
NEW
↓ start()
RUNNABLE
↓(抢synchronized锁失败)
BLOCKED
↓(抢到锁)
RUNNABLE
↓ wait() / join()
WAITING
↓ notify() / notifyAll() / 被唤醒后抢锁失败
BLOCKED
↓(抢到锁)
RUNNABLE
↓ sleep() / wait(time) / join(time)
TIMED_WAITING
↓ 时间到 / 被唤醒
RUNNABLE
↓ run()执行完毕
TERMINATED
四、常见问题
Q1:线程调用 sleep() 时,状态是什么?会释放锁吗?
状态:
TIMED_WAITING不会释放锁,只是暂停执行,锁依然被持有。
Q2:线程调用 wait() 后,状态是什么?被 notify() 后会直接运行吗?
调用
wait()后:进入WAITING状态,同时释放锁。被
notify()后:不会直接运行,而是需要重新抢锁。抢锁成功 → 变回
RUNNABLE;抢锁失败 → 进入BLOCKED状态。
Q3:线程处于 BLOCKED 状态时,能被 interrupt() 中断吗?
不能。
interrupt()只能中断处于WAITING/TIMED_WAITING状态的线程,抛出InterruptedException。
BLOCKED状态的线程只能等抢到锁,或者JVM退出才会结束。
Q4:为什么 synchronized 锁竞争失败会进入 BLOCKED,而 ReentrantLock 抢锁失败是 WAITING?
因为
ReentrantLock底层用的是LockSupport.park(),会让线程进入WAITING状态,而不是BLOCKED。
BLOCKED是synchronized内置锁特有的状态。
五、超简记忆口诀
- NEW 新生,RUNNABLE 就绪
- 抢锁失败 BLOCKED,主动等待 WAITING
- 限时等待 TIMED_WAITING,跑完 TERMINATED
- wait 放锁,sleep 不放锁,notify 后还要抢锁