线程状态及其转换与区别
线程的生命周期包含多个状态,不同状态之间的转换由线程调度和同步机制决定。以下是线程状态的详细说明、转换关系及阻塞与等待的区别:
一、线程的六种基本状态(以Java为例)
状态 | 描述 |
---|---|
NEW(新建) | 线程被创建但未启动(start() 尚未调用)。 |
RUNNABLE(可运行) | 线程已启动,可能正在运行或等待CPU时间片(包含操作系统的就绪和运行状态)。 |
BLOCKED(阻塞) | 线程因等待获取锁而无法进入同步块/方法(如其他线程持有锁)。 |
WAITING(等待) | 线程主动进入无限期等待,需其他线程显式唤醒(如调用 wait() 、join() )。 |
TIMED_WAITING(计时等待) | 线程在指定时间内等待(如 sleep(ms) 、wait(timeout) )。 |
TERMINATED(终止) | 线程执行完毕或异常退出。 |
二、状态转换关系与触发条件
NEW → RUNNABLE
• 触发条件:调用start()
方法启动线程。RUNNABLE ↔ RUNNING(操作系统层面)
• 运行 → 就绪:时间片用完或被更高优先级线程抢占(由操作系统调度决定)。
• 就绪 → 运行:被操作系统调度选中,分配CPU时间片。RUNNABLE → BLOCKED
• 触发条件:尝试进入同步块/方法时,锁已被其他线程持有。
• 示例:线程A持有锁,线程B尝试进入同步块时被阻塞。RUNNABLE → WAITING
• 触发条件:调用无超时的wait()
、join()
或LockSupport.park()
。
• 示例:线程调用object.wait()
主动释放锁并进入等待队列。RUNNABLE → TIMED_WAITING
• 触发条件:调用带超时的sleep(ms)
、wait(timeout)
或join(timeout)
。
• 示例:线程调用Thread.sleep(1000)
休眠1秒。BLOCKED → RUNNABLE
• 触发条件:锁被释放(如持有锁的线程退出同步块)。
• 示例:线程A释放锁后,线程B成功获取锁并恢复执行。WAITING/TIMED_WAITING → RUNNABLE
• 触发条件:
◦WAITING
:其他线程调用notify()
/notifyAll()
或发生中断。
◦TIMED_WAITING
:超时时间到或被唤醒。
• 示例:线程调用object.wait(5000)
,5秒后自动恢复。RUNNABLE → TERMINATED
• 触发条件:线程正常执行完毕或抛出未捕获异常。
三、阻塞(BLOCKED)与等待(WAITING)的区别
维度 | BLOCKED(阻塞) | WAITING(等待) |
---|---|---|
触发原因 | 被动等待锁(竞争同步资源失败)。 | 主动释放锁并等待条件(如调用 wait() )。 |
锁状态 | 未持有锁,等待获取锁。 | 已释放锁,等待其他线程唤醒。 |
唤醒机制 | 锁被释放时自动竞争。 | 需其他线程显式唤醒(notify() /notifyAll() )或中断。 |
典型场景 | 多线程竞争同一同步块。 | 生产者-消费者模型中的条件等待。 |
代码示例 | synchronized(obj) { ... } 竞争失败时。 |
synchronized(obj) { obj.wait(); } 。 |
四、状态转换流程图
start()
NEW ────────────────> RUNNABLE
│
│ 获取CPU时间片
↓
RUNNING (操作系统层面)
│
│ 时间片用完/被抢占
↓
RUNNABLE
│
├─ 尝试获取锁失败 ────> BLOCKED
│
├─ 调用 wait()/join() ──> WAITING
│
├─ 调用 sleep(ms) ────> TIMED_WAITING
│
└─ 执行完成/异常 ─────> TERMINATED
五、关键总结
BLOCKED 与 WAITING 的核心区别:
• BLOCKED 是线程被动等待锁,未持有锁;WAITING 是线程主动释放锁后等待条件。
• BLOCKED 由锁竞争触发,WAITING 由显式调用等待方法触发。状态转换的核心逻辑:
• 线程状态的切换依赖于锁机制、同步方法和操作系统调度。
•TIMED_WAITING
是带有超时的等待,可避免无限期阻塞。实际应用建议:
• 避免过度使用BLOCKED
(如减少锁竞争),优先考虑无锁数据结构。
• 使用WAITING
和TIMED_WAITING
实现线程协作时,需注意死锁和资源泄漏问题。
通过理解线程状态及其转换,开发者能更高效地设计并发程序,并快速定位死锁、资源竞争等问题。