外观
等待状态和阻塞状态的差异
⭐ 题目日期:
美团 - 2024/12/23
📝 题解:
在操作系统中,线程或进程的状态管理是理解并发和资源调度的核心。**等待状态(Waiting)和阻塞状态(Blocked)**虽然都表示线程或进程暂时无法执行,但它们的触发条件、行为及底层机制存在显著差异。以下是两者的详细对比:
1. 定义与触发条件
状态 | 触发条件 | 典型场景 |
---|---|---|
阻塞状态 | 线程因外部资源不可用(如I/O未完成、锁被占用等)而暂停执行。 | - 尝试获取已被其他线程持有的锁(如synchronized )。- 等待磁盘I/O完成。 |
等待状态 | 线程主动放弃CPU并等待某个事件触发(如条件变量、定时唤醒或信号通知)。 | - 调用Object.wait() 等待通知。- 调用 Thread.join() 等待其他线程结束。 |
2. 核心差异
维度 | 阻塞状态(Blocked) | 等待状态(Waiting) |
---|---|---|
触发方式 | 被动:由系统或外部资源限制导致。 | 主动:通过代码显式触发(如调用wait() 或join() )。 |
恢复条件 | 等待的资源变为可用(如锁释放、I/O完成)。 | 等待的事件被触发(如收到notify() 信号、超时或线程终止)。 |
状态转换 | 由操作系统内核管理,自动恢复。 | 需依赖其他线程的协作(如调用notify() )或超时机制。 |
在Java中的体现 | 线程处于BLOCKED 状态(如竞争锁失败)。 | 线程处于WAITING 或TIMED_WAITING 状态(如调用wait() )。 |
3. 底层机制
(1) 阻塞状态(Blocked)
- 资源竞争:线程因无法获取共享资源(如锁、I/O设备)被挂起。
- 内核介入:由操作系统调度器管理,资源可用时自动唤醒线程。
- 示例:
// 线程A持有锁,线程B尝试获取锁时被阻塞(BLOCKED) synchronized (lock) { // 临界区代码 }
(2) 等待状态(Waiting)
- 条件等待:线程主动进入等待,直到特定条件满足(如数据就绪、任务完成)。
- 协作唤醒:需其他线程显式唤醒(如
notify()
)或等待超时。 - 示例:
// 线程调用wait()进入等待状态(WAITING) synchronized (lock) { while (!condition) { lock.wait(); // 释放锁并等待 } // 条件满足后继续执行 }
4. Java线程状态模型
Java明确区分了阻塞和等待状态(通过Thread.State
枚举):
BLOCKED
:线程因竞争锁失败而阻塞(如进入synchronized
块时锁被占用)。WAITING
:线程因调用无超时的wait()
、join()
等方法进入无限期等待。TIMED_WAITING
:线程因调用带超时的sleep()
、wait(timeout)
等方法进入限期等待。
5. 状态转换流程
+------------------------+ 获取锁失败 +---------------------+
| RUNNABLE | -------------------> | BLOCKED |
+------------------------+ +---------------------+
| ^ |
| | 锁可用 |
| +-----------------------------------------+
|
| 调用wait()/join()/park()
v
+------------------------+ 超时或唤醒 +---------------------+
| WAITING | -------------------> | RUNNABLE |
| 或 TIMED_WAITING | <------------------- +---------------------+
+------------------------+ 调用sleep(timeout)
6. 关键问题与示例
(1) 阻塞状态的问题
- 死锁风险:多个线程互相持有对方所需资源,导致永久阻塞。
// 线程A持有锁1,请求锁2;线程B持有锁2,请求锁1 synchronized (lock1) { synchronized (lock2) { ... } // 死锁 }
(2) 等待状态的问题
- 无限等待:若未正确唤醒,线程可能永远挂起。
synchronized (lock) { lock.wait(); // 若没有其他线程调用lock.notify(),将永久等待 }
总结
特性 | 阻塞状态(Blocked) | 等待状态(Waiting) |
---|---|---|
触发原因 | 被动等待外部资源(如锁、I/O)。 | 主动等待事件触发(如条件变量、超时)。 |
恢复方式 | 资源可用时由系统自动唤醒。 | 需其他线程显式唤醒或超时。 |
协作性 | 无依赖其他线程的协作。 | 依赖其他线程的协作(如notify() )。 |
设计意义 | 处理资源竞争问题(同步互斥)。 | 实现线程间协调(生产者-消费者、任务依赖)。 |
实践建议
- 避免长时间阻塞:使用非阻塞I/O或超时机制(如
tryLock()
)。 - 谨慎使用等待:确保唤醒逻辑完备,避免无限等待。
- 优先使用高层工具:如
java.util.concurrent
包中的Condition
、CountDownLatch
等,简化线程协作。