核心区别一句话总结
sleep() 是让线程暂停执行,它不释放任何锁;而 wait() 是让线程等待,它会释放当前持有的锁。

详细对比表格
为了更直观地理解,我们通过一个表格来对比它们的主要区别:
| 特性 | sleep() (来自 Thread 类) |
wait() (来自 Object 类) |
|---|---|---|
| 所属类 | java.lang.Thread |
java.lang.Object |
| 锁的释放 | 不释放任何锁。 | 释放当前对象上的锁。 |
| 使用前提 | 可以在任何地方直接调用。 | 必须在同步代码块或同步方法中调用,否则会抛出 IllegalMonitorStateException。 |
| 唤醒方式 | 指定的时间自动苏醒。 被中断( interrupt())会抛出 InterruptedException。 |
被另一个线程调用 notify() 或 notifyAll() 唤醒。被中断( interrupt())会抛出 InterruptedException。指定超时时间( wait(long timeout))后自动苏醒。 |
| 目的 | 通常用于暂停一个线程一段时间,不涉及线程间的通信。 | 通常用于线程间通信,一个线程等待某个条件成立,其他线程改变条件后唤醒它。 |
| 异常 | 调用时被中断会抛出 InterruptedException。 |
调用时被中断会抛出 InterruptedException;在非同步上下文中调用会抛出 IllegalMonitorStateException。 |
代码示例:直观感受区别
通过一个简单的生产者-消费者模型,我们可以非常清楚地看到 wait() 和 sleep() 在行为上的巨大差异。
场景:一个共享的缓冲区,一个生产者,一个消费者。
使用 wait() 和 notify() (正确的方式)
这是经典的线程协作模式,当缓冲区满时,生产者等待;当缓冲区空时,消费者等待。
class SharedBuffer {
private int buffer = 0; // 缓冲区容量为1
// 生产者方法
public synchronized void produce() throws InterruptedException {
// 如果缓冲区有东西,就等待消费者消费
while (buffer > 0) {
System.out.println("生产者:缓冲区已满,等待...");
wait(); // 释放锁,并等待
}
// 被唤醒后,执行生产逻辑
buffer++;
System.out.println("生产者:生产了1个产品,缓冲区数量: " + buffer);
// 通知消费者可以消费了
notify(); // 唤醒在同一个对象上等待的线程
}
// 消费者方法
public synchronized void consume() throws InterruptedException {
// 如果缓冲区为空,就等待生产者生产
while (buffer == 0) {
System.out.println("消费者:缓冲区为空,等待...");
wait(); // 释放锁,并等待
}
// 被唤醒后,执行消费逻辑
buffer--;
System.out.println("消费者:消费了1个产品,缓冲区数量: " + buffer);
// 通知生产者可以生产了
notify(); // 唤醒在同一个对象上等待的线程
}
}
public class WaitNotifyExample {
public static void main(String[] args) {
SharedBuffer buffer = new SharedBuffer();
// 生产者线程
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
buffer.produce();
Thread.sleep(500); // 模拟生产耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 消费者线程
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
buffer.consume();
Thread.sleep(1000); // 模拟消费耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
输出分析:
你会发现生产者和消费者会交替执行,当生产者发现缓冲区满时,它会调用 wait() 并释放锁,这样消费者线程就能获得锁,进入 consume() 方法消费产品,消费完后,消费者调用 notify() 唤醒生产者,这是一个完美的协作过程。

错误地使用 sleep() (死锁的例子)
现在我们尝试用 sleep() 来实现同样的逻辑,看看会发生什么。
class BadSharedBuffer {
private int buffer = 0;
public synchronized void produce() throws InterruptedException {
// 错误:使用 sleep 代替 wait
if (buffer > 0) {
System.out.println("生产者:缓冲区已满,等待...");
Thread.sleep(1000); // 线程休眠,但**不释放锁**
}
buffer++;
System.out.println("生产者:生产了1个产品,缓冲区数量: " + buffer);
// 没有notify,因为sleep不涉及线程唤醒
}
public synchronized void consume() throws InterruptedException {
// 错误:使用 sleep 代替 wait
if (buffer == 0) {
System.out.println("消费者:缓冲区为空,等待...");
Thread.sleep(1000); // 线程休眠,但**不释放锁**
}
buffer--;
System.out.println("消费者:消费了1个产品,缓冲区数量: " + buffer);
}
}
public class SleepExample {
public static void main(String[] args) {
BadSharedBuffer buffer = new BadSharedBuffer();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
buffer.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
buffer.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
输出分析与问题:
- 假设生产者先运行,发现
buffer为 0,成功生产,buffer变为 1。 - 生产者再次运行,发现
buffer为 1(大于 0),于是进入if语句,调用Thread.sleep(1000)。 - 关键点:生产者虽然休眠了,但它仍然持有
BadSharedBuffer对象的锁。 - 消费者线程尝试获取锁来执行
consume(),但它被阻塞了,因为它拿不到锁。 - 1秒后,生产者苏醒,但因为它仍然持有锁,它会继续下一次循环,再次发现
buffer为 1,再次进入sleep,消费者线程永远拿不到锁。
结果:死锁! 这就是 sleep() 不释放锁可能导致的严重问题。
总结与最佳实践
sleep() |
wait() |
|
|---|---|---|
| 使用场景 | 时间控制:当你想让一个线程暂停一段固定的时间,不关心其他线程的状态时,每隔1秒执行一次任务。 | 线程协作:当一个线程需要等待另一个线程完成某个操作或某个条件满足时,生产者-消费者、等待队列。 |
| 记住的口诀 | sleep 是“睡一会儿”,自己醒了,不麻烦别人,也不放手。 |
wait 是“等一下”,告诉别人“等我”,并把手里的“锁”放下,等别人“通知” (notify) 后再继续。 |
- 如果你只是想让程序“停一下”,用
Thread.sleep()。 - 如果你需要让多个线程互相配合、互相等待,用
Object.wait()和Object.notify()/notifyAll()。
