一、为什么 wait() 必须放在 synchronized 同步块中?
❓先来个基本认知:
-
wait()是线程之间通信的方法。 -
它会让当前线程进入等待状态,并且释放掉它持有的“锁”,等待其他线程通过
notify()来唤醒它。
📌 重点:wait() 和 “锁” 是强相关的
举个例子你就明白了:
你有一个仓库(共享资源),有两个线程:
-
消费者线程:来仓库取东西。
-
生产者线程:往仓库放东西。
当消费者发现“仓库是空的”时,它要等生产者放货(即:wait)。
而生产者放货之后,会通知消费者“你可以来了”(即:notify)。
但是,如果这时候消费者没有获得锁就调用 wait() 会怎样?
new Object().wait(); // 直接抛异常 IllegalMonitorStateException!
因为你都没拿到钥匙(锁),你哪有资格说“我要在门口等着”(wait)?
✅ 所以为什么 wait() 要在同步块里?
因为:
-
只有拿到锁(进入了同步块),才有资格操作等待队列(wait/notify)。
-
为了防止线程错过通知,必须让检查资源的代码和 wait() 之间是“原子操作”。
💣 不加锁会出问题:Lost Wake-up(丢失通知)
比如:
if (count <= 0) {
wait();
}
如果你在判断完 count <= 0 之后,还没来得及 wait(),CPU 就切换了线程,另一个线程把货放了,发了 notify(),你还没 wait 呢!
结果你这边一调用 wait(),就睡过去了,而通知早就发了——你永远也醒不过来了!
这就叫 丢失唤醒(Lost Wake-up),而加 synchronized 就能避免这个问题,因为你两步操作(检查条件、决定等待)是“加锁”的,别人进不来,线程切换时不会打断你。
二、为什么 sleep() 不需要在同步块中?
🤔 本质区别:
-
sleep()是线程自己休息,和别人没关系。 -
它不会释放锁、也不管什么等待队列,只是当前线程“自觉休息一下”。
举个例子:
A线程抢到了锁,执行中调用了 Thread.sleep(1000) ——它休息,但不释放锁。
这时候 B线程来了,想进同步块,但没门,A线程还拿着钥匙。
所以:
sleep()是线程自我暂停,不释放资源,不需要通知别人,也不管别人状态,当然就不需要同步块了。
三、总结对比表:
| 对比点 | wait() | sleep() |
|---|---|---|
| 定义位置 | Object 类 | Thread 类 |
| 是否释放锁 | ✅ 会释放锁 | ❌ 不释放锁 |
| 是否需要同步块 | ✅ 必须在 synchronized 中调用 | ❌ 可以在任何地方调用 |
| 作用 | 线程间通信,进入等待队列等待唤醒 | 当前线程休息一会儿 |
| 唤醒机制 | 需要其他线程调用 notify() | 时间到了自动醒来 |
四、额外回答:为啥 wait() 在 Object 类里,sleep() 在 Thread 里?
-
wait()是操作对象锁(monitor)的,所以放在Object类中,每个对象都有锁。 -
sleep()是操作线程自己的行为,所以属于Thread类。
📌 最通俗一句话总结:
wait()是多人协作排队,必须“拉个队伍+发通知”,所以要锁;
sleep()是“我先眯一会儿”,不管别人,也不让出门钥匙,自然不用锁。

5287

被折叠的 条评论
为什么被折叠?



