一句话说透Java里面的wait、sleep的区别以及notify的运行过程

217 阅读2分钟

一、wait() 和 sleep() 的区别

1. 基本特性

特性wait()sleep()
所属类Object 类的方法Thread 类的方法
锁的行为释放对象锁(让其他线程能操作对象)不释放锁(线程抱着锁睡觉)
调用条件必须在 synchronized 代码块中调用任何地方直接调用
唤醒方式需要其他线程调用 notify() 或 notifyAll()时间到了自动醒,或被打断(interrupt()
用途线程间协作(如生产者-消费者)单纯让线程暂停一段时间

2. 举个现实例子

  • wait()  就像你去餐厅排队,服务员说“菜还没好,你先去旁边等着(释放锁),等菜好了我叫你(notify())”。
  • sleep()  就像你排队时玩手机,虽然站着不动(不释放锁),但时间到了继续排队。

3. 代码示例(文字描述)

  • wait() 使用
    线程A在同步块中调用 object.wait(),释放锁并等待;线程B在同步块中完成任务后调用 object.notify(),唤醒线程A。
  • sleep() 使用
    线程调用 Thread.sleep(1000),暂停1秒,不释放锁,其他线程无法进入同步块。

二、notify() 的运行过程

1. 核心作用

  • notify() :随机唤醒一个在相同对象上调用 wait() 的线程。
  • notifyAll() :唤醒所有在相同对象上等待的线程,但最终只有一个能抢到锁。

2. 运行流程

  1. 前提条件:必须在 synchronized 代码块中调用。
  2. 唤醒线程:从该对象的等待队列中选一个线程(notify())或所有线程(notifyAll())。
  3. 竞争锁:被唤醒的线程需要重新竞争对象锁,抢到锁的线程才能继续执行。
  4. 执行后续代码:被唤醒的线程从 wait() 处恢复执行。

3. 现实类比

  • 餐厅里服务员喊一声“菜好了”(notify()),所有等待的顾客(线程)中随机一人去取菜(抢锁),其他人继续等。

三、关键注意事项

  1. wait() 必须搭配 synchronized
    否则直接抛 IllegalMonitorStateException 异常。

    synchronized (lock) {  
        lock.wait(); // 正确用法  
    }  
    
  2. sleep() 不释放锁
    若在同步块内调用 sleep(),其他线程会被阻塞,容易导致性能问题。

  3. 虚假唤醒
    线程可能无缘无故被唤醒(即使没有 notify()),因此 wait() 通常要在循环中检查条件:

    while (条件不满足) {  
        lock.wait();  
    }  
    
  4. 中断处理
    wait() 和 sleep() 都可能被其他线程打断(interrupt()),需处理 InterruptedException


四、总结

  • wait() :用于线程协作,释放锁让其他线程干活,需搭配 notify() 使用。
  • sleep() :单纯暂停线程,不释放锁,适合定时任务。
  • notify() :唤醒等待线程,但需竞争锁,不是直接传递执行权。

口诀
「wait 释放锁,等通知来干活
sleep 抱锁睡,到点自然醒
notify 一声喊,抢到锁的才能干!」