wait() 和 sleep() 是 Java 多线程中两个非常容易混淆的方法,它们都会让当前线程暂停执行,但用途、机制和使用场景有本质区别。
核心区别一览
| 对比项 | wait() |
sleep() |
|---|---|---|
| 所属类 | Object |
Thread |
| 是否释放锁 | 释放持有的监视器锁 | 不释放锁 |
| 调用前提 | 必须在 synchronized 块/方法中 |
任意位置都可调用 |
| 唤醒方式 | notify() / notifyAll() 或超时 |
超时自动醒,或 interrupt() |
| 异常 | 抛 InterruptedException |
抛 InterruptedException |
| 主要用途 | 线程间通信/协作 | 单纯让线程暂停一段时间 |
一、wait()
wait() 定义在 java.lang.Object 上,因为它是**对象监视器(monitor)**机制的一部分。每个对象都有一把锁和一个等待队列。
关键特性
- 必须持有该对象的锁才能调用 ,否则抛
IllegalMonitorStateException。 - 调用后会释放锁,让其他线程有机会进入同步块。
- 线程进入该对象的 等待集(wait set) ,直到:
- 其他线程调用同一对象的
notify()/notifyAll() - 或者等待时间超时(
wait(long timeout))
- 其他线程调用同一对象的
- 被唤醒后不会立即执行 ,需要重新竞争锁,拿到锁后才从
wait()返回。
标准用法(生产者-消费者模式)
java
class Buffer {
private final Queue<Integer> queue = new LinkedList<>();
private final int CAPACITY = 5;
public synchronized void produce(int value) throws InterruptedException {
while (queue.size() == CAPACITY) {
wait(); // 队列满,释放锁并等待
}
queue.add(value);
notifyAll(); // 唤醒等待的消费者
}
public synchronized int consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 队列空,释放锁并等待
}
int value = queue.poll();
notifyAll(); // 唤醒等待的生产者
return value;
}
}
为什么用 while 而不是 if?
线程被唤醒后可能条件又不满足了(虚假唤醒 spurious wakeup ,或别的线程抢先消费了),所以必须循环检查条件:
java
while (条件不满足) {
wait();
}
二、sleep()
sleep() 是 Thread 的静态方法,作用是让当前线程暂停指定时间。
关键特性
- 调用时不会释放任何锁------如果持有锁,其他线程照样进不来。
- 到时间后线程回到就绪状态,等待 CPU 调度。
- 可以被
interrupt()打断,抛出InterruptedException。
java
Thread.sleep(1000); // 睡 1 秒
Thread.sleep(1000, 500000); // 1 秒 + 500 微秒(纳秒参数)
常见用途
- 模拟耗时操作、轮询间隔
- 让出 CPU 给其他线程
java
while (true) {
// 轮询检查任务
if (hasTask()) {
doTask();
}
Thread.sleep(500); // 每 500ms 检查一次
}
三、为什么 sleep 不释放锁?
看个例子就明白了:
java
synchronized (lock) {
Thread.sleep(5000); // 这 5 秒内,其他线程无法进入这个同步块
}
如果 sleep 释放了锁,同步语义就被破坏了------synchronized 保护的临界区可能被其他线程插入执行,逻辑就乱了。而 wait 的设计目的恰恰是主动让出锁去等别人做事,所以必须释放。
四、记忆口诀
- wait 是"等通知":让出锁,等别人叫醒我 → 用于线程协作
- sleep 是"打个盹":抱着锁睡,到点自己醒 → 用于延时
五、补充:现代替代方案
虽然 wait/notify 是基础,但实际开发中更推荐用 java.util.concurrent 包里的高级工具,它们更安全、更易读:
Condition(配合ReentrantLock)------ 替代wait/notify,支持多个等待队列BlockingQueue------ 直接解决生产者-消费者问题LockSupport.park()/unpark()------ 更底层的线程阻塞原语,Lock的底层实现
java
// 用 BlockingQueue 替代手写 wait/notify
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
queue.put(value); // 满了自动阻塞
int v = queue.take(); // 空了自动阻塞