在 Java 多线程编程中,sleep()
和 wait()
是两个容易混淆的方法,虽然它们都能让线程暂停执行,但底层机制和适用场景完全不同。以下是它们的核心区别、底层原理及使用场景的详细解析:
1. 核心区别
特性 | Thread.sleep() | Object.wait() |
---|---|---|
所属类 | Thread 类的静态方法 | Object 类的实例方法 |
锁的释放 | 不释放锁(线程继续持有锁) | 释放锁(线程进入等待队列,允许其他线程获取锁) |
调用条件 | 任何场景均可调用 | 必须在同步代码块(synchronized )内调用 |
唤醒机制 | 时间到期自动唤醒 | 需其他线程调用 notify() /notifyAll() 或超时唤醒 |
用途 | 单纯让线程休眠,不涉及线程间协调 | 线程间协作,等待特定条件满足(如生产者-消费者模型) |
异常处理 | 需捕获 InterruptedException | 需捕获 InterruptedException |
状态变化 | 进入 TIMED_WAITING 状态 | 进入 WAITING 或 TIMED_WAITING 状态(若设置超时) |
2. 底层机制
(1) Thread.sleep()
- 行为:让当前线程休眠指定时间(毫秒/纳秒),不释放任何锁。
- 线程状态:休眠期间线程进入
TIMED_WAITING
状态。 - 实现依赖:通过操作系统调度实现精确性(实际休眠时间可能略有偏差)。
(2) Object.wait()
- 行为:让当前线程释放对象锁,并进入等待状态,直到其他线程调用
notify()
/notifyAll()
或超时。 - 线程状态:进入
WAITING
(无超时)或TIMED_WAITING
(有超时)状态。 - 锁管理:调用前必须先获取对象锁(否则抛出
IllegalMonitorStateException
)。
3. 使用场景对比
场景 1:单纯暂停线程(使用 sleep()
)
// 让当前线程休眠 1 秒,不涉及锁的释放
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
}
场景 2:线程间协作(使用 wait()
)
// 生产者-消费者模型中的缓冲区操作
class Buffer {
private Queue<Integer> queue = new LinkedList<>();
private int capacity = 10;
public void produce(int value) throws InterruptedException {
synchronized (this) {
while (queue.size() == capacity) {
wait(); // 缓冲区满,释放锁并等待
}
queue.add(value);
notifyAll(); // 唤醒所有等待的消费者线程
}
}
public int consume() throws InterruptedException {
synchronized (this) {
while (queue.isEmpty()) {
wait(); // 缓冲区空,释放锁并等待
}
int value = queue.poll();
notifyAll(); // 唤醒所有等待的生产者线程
return value;
}
}
}
4. 关键注意事项
(1) 锁的释放
sleep()
:不释放锁,若在同步代码块中调用,其他线程无法进入该同步块。wait()
:释放锁,允许其他线程获取锁并执行代码。
(2) 中断处理
- 两者均可被中断(调用
thread.interrupt()
),抛出InterruptedException
。 - 最佳实践:捕获异常后,调用
Thread.currentThread().interrupt()
恢复中断状态。
(3) 虚假唤醒(Spurious Wakeup)
wait()
可能因底层操作系统原因意外唤醒(即使未调用notify()
)。- 解决方案:始终在循环中检查条件,而非使用
if
:synchronized (lock) { while (!condition) { // 必须用 while,不能用 if! lock.wait(); } // 执行任务 }
5. 性能与设计建议
- 优先使用
java.util.concurrent
工具:
避免直接使用wait()
/notify()
,改用Lock
、Condition
、BlockingQueue
等高级 API。Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); lock.lock(); try { while (!ready) { condition.await(); // 类似 wait(),但更灵活 } } finally { lock.unlock(); }
- 避免混合使用
sleep()
和锁:
在同步代码块中调用sleep()
会导致其他线程长时间阻塞。
总结
方法 | 核心目的 | 锁行为 | 适用场景 |
---|---|---|---|
sleep() | 线程暂停执行,不涉及协作 | 不释放锁 | 定时任务、模拟耗时操作 |
wait() | 线程间协作,等待条件满足 | 释放锁 | 生产者-消费者、多线程协调任务 |
最佳实践:
- 在需要线程协作时,优先使用
wait()
/notifyAll()
或Condition
。 - 在单纯需要延迟执行时,使用
sleep()
,但避免在持有锁时调用。 - 始终处理中断异常,确保程序健壮性。