一、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. 运行流程
- 前提条件:必须在
synchronized代码块中调用。 - 唤醒线程:从该对象的等待队列中选一个线程(
notify())或所有线程(notifyAll())。 - 竞争锁:被唤醒的线程需要重新竞争对象锁,抢到锁的线程才能继续执行。
- 执行后续代码:被唤醒的线程从
wait()处恢复执行。
3. 现实类比
- 餐厅里服务员喊一声“菜好了”(
notify()),所有等待的顾客(线程)中随机一人去取菜(抢锁),其他人继续等。
三、关键注意事项
-
wait()必须搭配synchronized:
否则直接抛IllegalMonitorStateException异常。synchronized (lock) { lock.wait(); // 正确用法 } -
sleep()不释放锁:
若在同步块内调用sleep(),其他线程会被阻塞,容易导致性能问题。 -
虚假唤醒:
线程可能无缘无故被唤醒(即使没有notify()),因此wait()通常要在循环中检查条件:while (条件不满足) { lock.wait(); } -
中断处理:
wait()和sleep()都可能被其他线程打断(interrupt()),需处理InterruptedException。
四、总结
wait():用于线程协作,释放锁让其他线程干活,需搭配notify()使用。sleep():单纯暂停线程,不释放锁,适合定时任务。notify():唤醒等待线程,但需竞争锁,不是直接传递执行权。
口诀:
「wait 释放锁,等通知来干活
sleep 抱锁睡,到点自然醒
notify 一声喊,抢到锁的才能干!」