核心区别一览表
为了让你先有一个宏观的认识,我们用一个表格来总结它们最核心的区别:

| 特性 | sleep() (来自 Thread 类) |
wait() (来自 Object 类) |
|---|---|---|
| 所属类 | java.lang.Thread |
java.lang.Object |
| 锁行为 | 不释放当前持有的锁 | 释放当前持有的锁 |
| 唤醒方式 | 自动唤醒(指定时间后) | 必须被唤醒(需要另一个线程调用 notify() 或 notifyAll()) |
| 使用位置 | 可以在任何地方调用 | 只能在 synchronized 代码块或方法中调用 |
| 用途 | 暂停线程执行,通常用于控制执行节奏或轮询 | 线程间通信和协调,让线程进入等待状态,等待某个条件满足 |
| 异常 | InterruptedException |
InterruptedException、IllegalMonitorStateException |
详细解析
下面我们对每个方法进行深入剖析。
sleep() 方法
sleep() 是 Thread 类中的一个静态方法。
基本用法:
try {
// 让当前线程暂停 1000 毫秒(1秒)
Thread.sleep(1000);
} catch (InterruptedException e) {
// 如果线程在睡眠期间被中断,会抛出 InterruptedException
e.printStackTrace();
}
关键特性:

-
不释放锁:这是
sleep()和wait()最根本的区别,当一个线程调用sleep()进入睡眠时,它依然持有它当前所拥有的任何锁,这意味着,即使它“睡着了”,其他需要这个锁的线程也无法进入相应的同步代码块。示例场景:
public class SleepExample { private static final Object lock = new Object(); public static void main(String[] args) { new Thread(() -> { synchronized (lock) { System.out.println("线程 A 获取了锁,准备睡眠 2 秒..."); try { Thread.sleep(2000); // 线程 A 睡觉,但依然持有锁 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程 A 睡眠结束,释放锁。"); } }).start(); new Thread(() -> { try { // 等待一小会儿,确保线程 A 已经拿到锁并开始睡眠 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock) { System.out.println("线程 B 尝试获取锁,但被线程 A 阻塞了!"); } }).start(); } }执行结果分析:
- 线程 A 先获取锁,打印信息,然后调用
sleep(2000)。 - 线程 B 尝试获取锁,但由于线程 A 虽然在睡觉,但没有释放锁,所以线程 B 会被阻塞,无法进入
synchronized代码块。 - 2秒后,线程 A 醒来,打印信息,然后释放锁,线程 B 才能获得锁并执行。
- 线程 A 先获取锁,打印信息,然后调用
-
自动唤醒:
sleep()会指定一个睡眠时间,时间一到,线程会自动从TIMED_WAITING状态转换为RUNNABLE状态,等待 JVM 调度器再次分配 CPU 时间片。
(图片来源网络,侵删) -
可中断:在睡眠期间,如果其他线程调用了该线程的
interrupt()方法,sleep()会立即抛出InterruptedException,并中断睡眠,线程的中断状态会被清除。
主要用途:
- 控制执行频率:在一个轮询任务中,每隔一段时间检查一次条件,避免 CPU 空转。
- 模拟耗时操作:在测试或演示代码中模拟一些需要时间的操作。
wait() 方法
wait() 是 Object 类中的一个实例方法,这意味着任何 Java 对象都可以调用 wait() 方法。
基本用法:
synchronized (someObject) {
try {
// 让当前线程等待,直到被唤醒或被中断
someObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
关键特性:
-
释放锁:当一个线程在
synchronized代码块中调用wait()时,它会立即释放它所持有的synchronized代码块或方法对应的锁,该线程会进入WAITING状态,并等待在someObject的等待队列上。 -
必须被唤醒:调用
wait()的线程不会自己醒来,它必须由另一个线程显式地调用同一个对象的notify()或notifyAll()方法来唤醒。notify():随机唤醒在该对象等待队列上的一个线程。notifyAll():唤醒在该对象等待队列上的所有线程,这些线程会竞争锁,最终只有一个能获取到锁并继续执行。
-
只能在同步代码块中调用:
wait()必须在synchronized上下文中调用,如果在非同步代码块中调用,会抛出IllegalMonitorStateException,这是因为wait()/notify()机制是建立在锁的基础上的,线程必须先持有锁,才能释放锁并等待。 -
可中断:和
sleep()一样,等待中的线程如果被interrupt(),也会抛出InterruptedException并从等待状态中退出。
主要用途:
- 线程间通信:这是
wait()的核心用途,它使得一个线程可以等待某个条件成立,而另一个线程在条件满足后可以通知它。 - 生产者-消费者模型:这是最经典的例子。
- 消费者线程:检查共享缓冲区(一个对象)是否为空,如果为空,就调用
buffer.wait(),释放锁并等待。 - 生产者线程:生产一个数据放入缓冲区,然后调用
buffer.notifyAll(),唤醒可能在等待的消费者线程。
- 消费者线程:检查共享缓冲区(一个对象)是否为空,如果为空,就调用
示例场景:生产者-消费者
public class ProducerConsumerExample {
private static final Object lock = new Object();
private static int item = 0;
public static void main(String[] args) {
// 消费者线程
new Thread(() -> {
synchronized (lock) {
System.out.println("消费者:检查商品...");
if (item == 0) {
System.out.println("消费者:没有商品,等待生产...");
try {
lock.wait(); // 释放锁,等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者:商品已到货,开始消费!商品数量: " + item);
item = 0;
}
}).start();
// 生产者线程
new Thread(() -> {
try {
// 确保消费者先启动并进入等待
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock) {
System.out.println("生产者:正在生产商品...");
item = 1;
System.out.println("生产者:商品生产完毕,通知消费者!");
lock.notify(); // 唤醒一个等待的线程
}
}).start();
}
}
执行结果分析:
- 消费者线程启动,获取锁,发现
item为 0,调用lock.wait(),释放锁并进入等待状态。 - 生产者线程启动,1秒后获取锁,将
item设为 1,然后调用lock.notify()。 notify()唤醒了消费者线程,消费者线程从WAITING状态变为BLOCKED状态,等待再次获取lock。- 生产者线程执行完毕,释放
lock。 - 消费者线程重新获取
lock,从wait()方法之后继续执行,打印消费信息。
总结与类比
为了更好地记忆,我们可以用一个生活中的类比来区分它们:
-
sleep()就像你上床睡觉:- 你在自己的床上睡(不释放你当前占据的资源/锁)。
- 你定了个闹钟(指定了时间),闹钟响了(时间到自动醒来)。
- 如果有人突然大声叫你(被
interrupt()),你也会被吵醒。
-
wait()就像在餐厅等位:- 你去餐厅发现没位置了,你对服务员说:“没位置的话,请叫我”(调用
wait(),释放餐桌这个“锁”)。 - 你不能自己站起来说“有位置了”(不能自己醒来)。
- 服务员看到有空位了,会喊你的号码(另一个线程调用
notify()),你被叫醒后去抢座位(重新获取锁)。 - 服务员也可能喊“所有号码的顾客请过来”(
notifyAll()),大家一起抢座位。
- 你去餐厅发现没位置了,你对服务员说:“没位置的话,请叫我”(调用
如何选择?
- 如果你只是想让线程暂停一段时间,而不涉及与其他线程的锁交互,使用
Thread.sleep()。 - 如果你的线程需要等待某个条件,而这个条件由其他线程改变,并且在这个过程中需要释放锁以允许其他线程去改变这个条件,那么使用
Object.wait()和notify()/notifyAll()。
在现代 Java 并发编程中,更推荐使用 java.util.concurrent 包中的工具,如 Lock 和 Condition,它们提供了比 synchronized、wait() 和 notify() 更强大和灵活的线程控制能力,但 wait() 和 notify() 仍然是理解并发原理的基础。
