核心概念一句话总结
sleep()(睡眠):是Thread类的一个静态方法,它让当前线程进入“休眠”状态,暂停指定的时间,但不会释放任何锁,它像一个“闹钟”,时间到了线程就会自动苏醒。wait()(等待):是Object类的一个方法,它让当前线程进入该对象的“等待池”,会立即释放当前持有的锁,它像一个“人质”,必须由其他线程(通常是生产者)通过notify()或notifyAll()来“解救”才能苏醒。
详细对比表格
| 特性 | sleep(long millis) |
wait() / wait(long timeout) |
|---|---|---|
| 所属类 | java.lang.Thread |
java.lang.Object |
| 作用 | 让当前线程暂停执行指定毫秒数。 | 让当前线程等待,直到其他线程调用该对象的 notify() 或 notifyAll()。 |
| 锁的释放 | 不释放锁,线程在睡眠期间仍然持有监视器锁。 | 立即释放锁,这是 wait() 和 sleep() 最核心的区别。 |
| 唤醒方式 | 自动唤醒,时间到了,线程会自动从 Runnable 状态变为 Runnable 状态,等待 CPU 调度。 |
被动唤醒,必须由其他线程调用 notify() / notifyAll() 来唤醒,也可以设置超时时间,超时后自动唤醒。 |
| 使用位置 | 可以在任何地方调用,因为它是一个静态方法。 | 只能在同步代码块或同步方法中调用,即,必须在持有锁的情况下才能调用 wait(),否则会抛出 IllegalMonitorStateException。 |
| 异常处理 | 只抛出 InterruptedException。 |
抛出 InterruptedException 和 IllegalMonitorStateException。 |
| 本质 | 是线程级别的操作,它只是让线程暂停,不关心锁的状态。 | 是对象级别的操作,它与对象的监视器锁(锁机制)紧密相关。 |
代码示例演示区别
通过一个经典的“生产者-消费者”模型,我们可以非常清晰地看到 wait() 和 notify() 的作用,以及为什么 sleep() 在这个场景下不适用。

场景:一个共享的缓冲区,生产者生产数据,消费者消费数据。
使用 wait() 和 notify() 的正确方式
这是实现线程间协作的标准方式。
共享资源类 (Resource.java)
public class Resource {
private String name;
private int count = 1;
private boolean flag = false; // 标记资源是否可用
// 生产者
public synchronized void produce(String name) {
// 如果资源已经存在,生产者就等待
while (flag) { // 使用 while 而不是 if,防止虚假唤醒
try {
wait(); // 释放锁,并进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name + "---" + count++;
System.out.println(Thread.currentThread().getName() + "...生产者... " + this.name);
flag = true;
notifyAll(); // 唤醒在等待池中的所有线程(可能是消费者或其他生产者)
}
// 消费者
public synchronized void consume() {
// 如果资源不存在,消费者就等待
while (!flag) {
try {
wait(); // 释放锁,并进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "...消费者... " + this.name);
flag = false;
notifyAll(); // 唤醒在等待池中的所有线程(可能是生产者或其他消费者)
}
}
生产者线程 (Producer.java)
public class Producer implements Runnable {
private Resource resource;
public Producer(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
while (true) {
resource.produce("商品");
}
}
}
消费者线程 (Consumer.java)

public class Consumer implements Runnable {
private Resource resource;
public Consumer(Resource resource) {
this.resource = resource;
}
@Override
public void run() {
while (true) {
resource.consume();
}
}
}
测试类 (WaitSleepDemo.java)
public class WaitSleepDemo {
public static void main(String[] args) {
Resource resource = new Resource();
new Thread(new Producer(resource), "生产者A").start();
new Thread(new Consumer(resource), "消费者B").start();
}
}
执行流程分析:
- 假设
生产者A先运行,它调用resource.produce(),因为flag为false,所以它不会进入while循环,直接生产数据,并将flag设为true,然后调用notifyAll()。 消费者B运行,调用resource.consume(),因为flag为true,所以它消费数据,并将flag设为false,然后调用notifyAll()。- 当
生产者A再次尝试运行时,flag为false,它再次生产... 如此循环。 - 关键点:当
生产者A调用wait()时,它会释放resource对象的锁,这时消费者B就能成功获取到锁,进入consume()方法,反之亦然。它们通过锁和wait/notify实现了完美的协作。
为什么不能用 sleep() 替代 wait()?
如果我们错误地在 Resource 类中使用 sleep(),会发生什么?
错误的代码示例 (仅修改 produce 和 consume 方法)

// 错误的 produce
public synchronized void produce(String name) {
if (flag) { // 使用 if 而不是 while
try {
System.out.println("生产者发现资源已满,准备睡觉...");
Thread.sleep(100); // 错误!使用 sleep
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// ... 生产逻辑 ...
flag = true;
notifyAll();
}
// 错误的 consume
public synchronized void consume() {
if (!flag) {
try {
System.out.println("消费者发现资源为空,准备睡觉...");
Thread.sleep(100); // 错误!使用 sleep
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// ... 消费逻辑 ...
flag = false;
notifyAll();
}
执行流程分析:
生产者A运行,生产一个商品,flag变为true。生产者A再次运行,发现flag为true,于是它调用Thread.sleep(100)。注意:它并没有释放锁!消费者B想要运行,但它无法获取resource对象的锁,因为生产者A还在同步代码块里睡觉!- 结果就是:生产者A 睡着了,并且一直霸占着锁,导致消费者B 永远无法运行,程序卡死。
这个例子完美地证明了:sleep() 不释放锁,因此在需要让出资源给其他线程的协作场景下,它完全无效。
总结与最佳实践
-
功能定位:
sleep():用于控制执行节奏,当你想让一个线程暂停一段固定时间,但不希望它影响其他线程时使用,一个轮询任务,每秒检查一次状态。wait()/notify():用于线程间通信与协作,当一个线程需要等待某个条件满足时(如资源可用、数据就绪),使用wait()让出锁并等待;当条件满足时,其他线程使用notify()来唤醒它。
-
记住核心区别:
sleep()是 Thread 的,wait()是 Object 的。sleep()不释放锁,wait()释放锁。sleep()自动醒,wait()被动醒。
-
现代替代方案: 在 Java 5 之后,
java.util.concurrent.locks包提供了更强大、更灵活的锁机制(如ReentrantLock),以及与Condition对象配合的await()和signal()方法,它们在功能上与wait()和notify()类似,但提供了更高级的功能,比如可以创建多个Condition(相当于多个等待队列),并且避免了notifyAll()带来的“伪唤醒”问题,在复杂的并发场景下,推荐使用ReentrantLock和Condition。
希望这个详细的解释能帮助你彻底理解 wait() 和 sleep() 的区别!
