这是一个非常经典且重要的面试题,理解它们的区别对于掌握 Java 并发编程至关重要。

一句话概括核心区别:
sleep()是让线程暂停执行,它不释放任何锁,它像一个“霸道”的同事,说“我休息10分钟,别叫我,也别想动我的东西”。wait()是让线程等待,它会释放对象锁,它像一个“有礼貌”的同事,说“我需要这个东西,你先用吧,用完了告诉我一声(notify()),我再用”。
下面我们从多个维度进行详细对比。
核心区别对比表
| 特性 | sleep() |
wait() |
|---|---|---|
| 所属类 | java.lang.Thread 的静态方法 |
java.lang.Object 的实例方法 |
| 锁的释放 | 不释放任何锁 | 释放当前对象的锁 |
| 使用位置 | 可以在任何地方使用 | 只能在同步代码块或同步方法中使用 |
| 唤醒方式 | 自动唤醒,到达指定时间后 | 必须被其他线程唤醒(notify() 或 notifyAll()) |
| 异常处理 | 不抛出 InterruptedException |
抛出 InterruptedException |
| 用途 | 一般用于暂停执行,不涉及线程间通信 | 主要用于线程间通信和同步 |
| 调用者 | 任意线程 | 获取了对象锁的线程 |
详细解释
sleep() 方法
sleep() 是 Thread 类的一个静态方法。
语法:

public static native void sleep(long millis) throws InterruptedException;
工作原理:
- 当一个线程调用
sleep()时,它会让出 CPU 的使用权,进入阻塞状态。 - 在指定的
millis毫秒时间过后,该线程会自动从阻塞状态转变为就绪状态,等待 CPU 再次调度执行。 - 最关键的一点:在整个
sleep期间,该线程不会释放它所持有的任何锁。
使用场景:
- 通常用于简单的、不需要线程间交互的延迟,模拟一个耗时操作,或者实现一个简单的轮询机制。
示例代码:
public class SleepExample {
public static void main(String[] args) {
Runnable task = () -> {
synchronized (SleepExample.class) { // 获取锁
System.out.println("线程 " + Thread.currentThread().getName() + " 获取了锁,准备进入 sleep");
try {
// 睡眠2秒,不释放锁
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程 " + Thread.currentThread().getName() + " 从 sleep 中醒来,继续执行");
}
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
t1.start();
t2.start();
}
}
运行结果分析:
你会看到 Thread-1 先打印信息,Thread-2 不会立即执行,而是要等到 Thread-1 的 sleep(2000) 结束后,Thread-1 打印完唤醒信息并释放锁,Thread-2 才能获取锁并执行,这证明了 sleep() 没有释放锁。

wait() 方法
wait() 是 Object 类的一个实例方法,这意味着任何 Java 对象都可以调用 wait() 方法。
语法:
public final void wait() throws InterruptedException; public final native void wait(long timeout) throws InterruptedException;
工作原理:
wait()必须在同步代码块(synchronized)中调用,因为一个线程只有在持有某个对象的锁时,才能让其他线程等待该对象的状态变化。- 当一个线程调用
obj.wait()时,它会立即释放obj对象的锁,然后进入该对象的等待队列,进入阻塞状态。 - 它不会自动醒来,必须由另一个线程调用
obj.notify()或obj.notifyAll()来唤醒它。 - 当被唤醒后,它会尝试重新获取
obj对象的锁,一旦获取到锁,它就会从wait()调用的地方继续执行。
使用场景:
wait()/notify()是 Java 中实现生产者-消费者、任务队列等经典并发模式的基石,它用于一个线程通知另一个线程某个条件已经满足。
示例代码:
public class WaitNotifyExample {
public static void main(String[] args) {
Object lock = new Object(); // 共享锁对象
// 消费者线程
new Thread(() -> {
synchronized (lock) {
System.out.println("消费者:等待商品...");
try {
lock.wait(); // 释放锁,进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者:被唤醒,开始消费商品!");
}
}, "Consumer").start();
// 生产者线程
try {
Thread.sleep(1000); // 模拟生产耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
synchronized (lock) {
System.out.println("生产者:生产好了商品,通知消费者!");
lock.notify(); // 唤醒在lock上等待的线程
}
}, "Producer").start();
}
}
运行结果分析:
Consumer线程启动,获取lock,打印信息,然后调用lock.wait(),释放lock并进入等待状态。Producer线程启动,等待1秒后,获取lock,打印信息,然后调用lock.notify()。notify()唤醒Consumer线程。Producer线程继续执行完同步代码块后释放lock。Consumer线程从等待状态被唤醒,重新获取lock,然后从wait()方法之后继续执行,打印消费信息。
总结与最佳实践
| 特性 | sleep() |
wait() |
|---|---|---|
| 一句话总结 | 让线程“休息”一会儿,但抱着锁不放 | 让线程“等待”,并放下锁,等别人叫醒 |
| 核心用途 | 延迟、暂停 | 线程间通信与同步 |
| 关键约束 | 无 | 必须在 synchronized 代码块中 |
如何选择?
- 如果你只是想让当前线程暂停一段时间,不关心其他线程的状态,也不需要与它们交互,请使用
Thread.sleep(),每隔一秒检查一次某个标志位。 - 如果你需要根据某个共享资源的状态来协调多个线程的执行顺序,即一个线程需要等待另一个线程完成某个操作后再继续,请使用
Object.wait()和Object.notify()。
现代并发工具
值得注意的是,wait()/notify() 机制相对底层且容易出错(忘记在 while 循环中检查条件,导致虚假唤醒问题),在现代 Java 开发中,我们更推荐使用更高级、更安全的并发工具,它们在内部已经帮我们处理了这些复杂性:
java.util.concurrent.locks.Condition: 可以看作是wait()/notify()的高级版本,一个锁可以绑定多个Condition,功能更强大。java.util.concurrent.BlockingQueue: 实现了生产者-消费者模式的队列,如ArrayBlockingQueue、LinkedBlockingQueue,它们内部已经实现了线程安全的等待和通知机制,是实际开发中的首选。java.util.concurrent.CountDownLatch/CyclicBarrier/Semaphore: 这些工具类提供了更丰富的同步控制能力。
希望这个详细的解释能帮助你彻底理解 wait() 和 sleep() 的区别!
