文章目录
-
- [一、先说结论:wait vs sleep 对锁和 CPU 的影响](#一、先说结论:wait vs sleep 对锁和 CPU 的影响)
- [二、锁的释放:wait 释放,sleep 不释放](#二、锁的释放:wait 释放,sleep 不释放)
-
- [wait() 释放锁](#wait() 释放锁)
- [sleep() 不释放锁](#sleep() 不释放锁)
- [三、CPU 的释放:两者都释放](#三、CPU 的释放:两者都释放)
- [四、完整对比:锁 + CPU 的四种组合](#四、完整对比:锁 + CPU 的四种组合)
- 五、实际场景中的正确用法
-
- [场景一:线程间协作 → 用 wait/notify](#场景一:线程间协作 → 用 wait/notify)
- [场景二:延迟执行 → 用 sleep](#场景二:延迟执行 → 用 sleep)
- [场景三:定时任务 → 用 sleep 的更优替代](#场景三:定时任务 → 用 sleep 的更优替代)
- 六、面试高频追问
- [wait vs sleep 全景](#wait vs sleep 全景)
- 回答技巧与点评
wait 和 sleep 都能让线程"停下来",但它们对锁和 CPU 的处理完全不同。面试官最爱问:"wait 释放锁吗?sleep 释放锁吗?它们释放 CPU 吗?"
搞不清这三个问题,写出的并发代码可能暗藏死锁或性能隐患。
一、先说结论:wait vs sleep 对锁和 CPU 的影响
| 维度 | wait() | sleep() |
|---|---|---|
| 释放锁 | ✅ 释放 | ❌ 不释放 |
| 释放 CPU | ✅ 释放 | ✅ 释放 |
| 所属类 | Object | Thread |
| 使用条件 | 必须在 synchronized 中 | 任意位置 |
| 唤醒方式 | notify/notifyAll | 超时自动醒 |
| 用途 | 线程间通信/协作 | 延时/定时 |
一句话记住:wait 是"让出锁 + 让出 CPU",sleep 是"不让锁 + 让出 CPU"------wait 是主动让路,sleep 是闭眼休息但占着茅坑。
二、锁的释放:wait 释放,sleep 不释放
wait() 释放锁
java
synchronized (lock) {
while (!condition) {
lock.wait(); // 释放 lock,其他线程可以获取 👈
}
}
为什么 wait 要释放锁? 因为 wait 的设计目的是"等待某个条件成立"------如果你不释放锁,其他线程怎么修改条件?
生活类比: 你在银行柜台等材料(wait),你不离开柜台(不释放锁)的话,别人怎么帮你准备材料?
sleep() 不释放锁
java
synchronized (lock) {
Thread.sleep(5000); // 5 秒内,其他线程拿不到 lock 👈
// 其他线程只能在外面等...
}
危险: 在 synchronized 块中 sleep,其他线程在这段时间内完全无法获取锁,可能造成性能问题甚至死锁。
生活类比: 你在公厕里睡觉(sleep),门锁着------别人再急也进不来,你占着坑不拉屎。
三、CPU 的释放:两者都释放
wait 和 sleep 都会让出 CPU,当前线程不会消耗 CPU 时间片:
java
// wait:线程进入 WAITING,不消耗 CPU
lock.wait(); // CPU 可以调度其他线程
// sleep:线程进入 TIMED_WAITING,不消耗 CPU
Thread.sleep(1000); // CPU 可以调度其他线程
对比: 如果你在代码中写"忙等待",那才是不释放 CPU:
java
// ❌ 忙等待:疯狂消耗 CPU!
while (!condition) {
// 一直转圈检查,CPU 跑满
}
// ✅ 正确等待:释放 CPU
synchronized (lock) {
while (!condition) {
lock.wait(); // 不消耗 CPU
}
}
生活类比: wait/sleep 是"去休息室等",忙等待是"在门口一直敲门"------前者省力,后者又吵又累。
四、完整对比:锁 + CPU 的四种组合
| 操作 | 释放锁? | 释放 CPU? | 线程状态 |
|---|---|---|---|
| wait() | ✅ | ✅ | WAITING |
| sleep() | ❌ | ✅ | TIMED_WAITING |
| 忙等待 | ❌ | ❌ | RUNNABLE |
| 持锁执行 | ❌ | ❌ | RUNNABLE |
最危险的是"sleep + synchronized": 不释放锁又不做有用功,纯粹浪费资源。
五、实际场景中的正确用法
场景一:线程间协作 → 用 wait/notify
java
class ProducerConsumer {
private final List<String> queue = new ArrayList<>();
private final int MAX = 10;
// 生产者
synchronized void produce(String item) throws InterruptedException {
while (queue.size() == MAX) {
wait(); // 队列满 → 等待(释放锁,让消费者获取) 👈
}
queue.add(item);
notifyAll(); // 通知消费者
}
// 消费者
synchronized String consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 队列空 → 等待(释放锁,让生产者获取) 👈
}
String item = queue.remove(0);
notifyAll(); // 通知生产者
return item;
}
}
关键: wait 释放锁使得生产者和消费者能交替执行。
场景二:延迟执行 → 用 sleep
java
// 轮询场景:每隔 5 秒检查一次
while (running) {
checkStatus();
Thread.sleep(5000); // 👈 不要放在 synchronized 中!
}
// ❌ 错误:在 synchronized 中 sleep
synchronized (lock) {
checkStatus();
Thread.sleep(5000); // 其他线程 5 秒内拿不到锁! 👈
}
场景三:定时任务 → 用 sleep 的更优替代
java
// ❌ sleep 不精确
Thread.sleep(5000);
// ✅ ScheduledExecutorService 更优
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> checkStatus(), 0, 5, TimeUnit.SECONDS);
六、面试高频追问
Q:wait 被唤醒后立刻执行吗?
A:不是。被唤醒后要重新竞争锁,获取锁后才继续执行。从 WAITING → BLOCKED(等锁)→ RUNNABLE(获取锁)。
Q:sleep 能被 interrupt 吗?
A:能。interrupt() 会中断 sleep,抛出 InterruptedException。
Q:LockSupport.park() 释放锁吗?
A:不释放。park() 只是阻塞线程,不涉及锁操作。ReentrantLock 的 lock() 内部用 park() 等待,但锁的获取和释放是 AQS 管理的,不是 park 的职责。
wait vs sleep 全景
wait vs sleep 全景
锁的释放
├── wait() → 释放锁(让其他线程能修改条件)
└── sleep() → 不释放锁(持锁睡觉,别人进不来)
CPU 的释放
├── wait() → 释放 CPU(线程进入 WAITING)
├── sleep() → 释放 CPU(线程进入 TIMED_WAITING)
└── 忙等待 → 不释放 CPU(疯狂空转)
核心区别
├── wait 是协作机制(等条件,释放锁让别人改条件)
├── sleep 是延时机制(等时间,不关心别人做什么)
└── 不要在 synchronized 中 sleep!
口诀:wait 释放锁和 CPU,sleep 只放 CPU 不放锁,
wait 用于线程协作,sleep 用于延时等待,
synchronized 里别 sleep,占着锁睡觉最坑人。
回答技巧与点评
标准回答
wait() 会释放锁和 CPU 资源,sleep() 只释放 CPU 资源不释放锁。wait() 必须在 synchronized 代码块中调用,调用后线程进入 WAITING 状态并释放对象锁,等待其他线程调用 notify 唤醒;sleep() 可以在任何位置调用,线程进入 TIMED_WAITING 状态,但不释放已持有的锁。因此不要在 synchronized 块中调用 sleep,否则其他线程在 sleep 期间无法获取锁。
加分回答
- 设计动机的区别:wait 是"条件等待"------等某个条件成立,释放锁是为了让其他线程能修改条件;sleep 是"时间等待"------纯粹等一段时间,和锁无关。两者的设计目的完全不同,wait 是线程间协作原语,sleep 是延时工具
- join() 的本质:join() 底层也是 wait()------当前线程在目标线程对象上 wait(),目标线程终止时 JVM 自动调用 notifyAll()。所以 join() 也会释放锁(虽然我们一般不在 synchronized 中调 join)
- Lock 的 await/signal:Condition.await() 等价于 Object.wait(),也会释放 Lock 锁;Condition.signal() 等价于 Object.notify()。但 Condition 更灵活------支持多个等待队列、超时等待、响应中断等
面试官点评
这道题考的是你对并发原语精确语义的理解。能清楚区分"释放锁"和"释放 CPU"是两个独立维度,并给出 wait/sleep 在这两个维度上的不同表现,才算及格。面试官最想听到的是:你不只是背结论,还能解释为什么 wait 要释放锁(设计目的决定),以及 synchronized 中 sleep 的危害。这种理解深度在排查死锁和性能问题时至关重要。
内容有帮助?点赞、收藏、关注三连!评论区等你 💪