孤舟笔记 并发篇二十八 wait和sleep是否会触发锁的释放及CPU资源的释放?这个区别面试必考

文章目录

    • [一、先说结论: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 期间无法获取锁。

加分回答
  1. 设计动机的区别:wait 是"条件等待"------等某个条件成立,释放锁是为了让其他线程能修改条件;sleep 是"时间等待"------纯粹等一段时间,和锁无关。两者的设计目的完全不同,wait 是线程间协作原语,sleep 是延时工具
  2. join() 的本质:join() 底层也是 wait()------当前线程在目标线程对象上 wait(),目标线程终止时 JVM 自动调用 notifyAll()。所以 join() 也会释放锁(虽然我们一般不在 synchronized 中调 join)
  3. Lock 的 await/signal:Condition.await() 等价于 Object.wait(),也会释放 Lock 锁;Condition.signal() 等价于 Object.notify()。但 Condition 更灵活------支持多个等待队列、超时等待、响应中断等
面试官点评

这道题考的是你对并发原语精确语义的理解。能清楚区分"释放锁"和"释放 CPU"是两个独立维度,并给出 wait/sleep 在这两个维度上的不同表现,才算及格。面试官最想听到的是:你不只是背结论,还能解释为什么 wait 要释放锁(设计目的决定),以及 synchronized 中 sleep 的危害。这种理解深度在排查死锁和性能问题时至关重要。

原文阅读


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

相关推荐
落魄江湖行17 小时前
孤舟笔记 并发篇二十二 线程池是如何回收线程的?核心线程和非核心线程的回收逻辑大不相同
java并发·春招·孤舟笔记·线程池是如何回收线程的
落魄江湖行1 天前
孤舟笔记 并发篇二十五 当任务数超过核心线程数时,如何让任务不进入队列?线程池调优的经典问题
java并发·春招·孤舟笔记·当任务数超过核心线程数时
落魄江湖行1 天前
孤舟笔记 并发篇二十三 线程池是如何实现线程复用的?Worker循环取任务的秘密远比你想象的精巧
java并发·春招·孤舟笔记
落魄江湖行3 天前
孤舟笔记 并发篇十一 行锁、间隙锁、临键锁傻傻分不清?MySQL InnoDB的锁其实就这三板斧
mysql·java并发·春招·孤舟笔记
落魄江湖行3 天前
孤舟笔记 并发篇十 ReentrantLock的公平锁和非公平锁是怎么实现的?这个设计藏着大智慧
java并发·春招·孤舟笔记
逻辑驱动的ken4 天前
Java高频面试考点场景题17
开发语言·jvm·面试·求职招聘·春招
逻辑驱动的ken7 天前
Java高频面试考点场景题14
java·开发语言·深度学习·面试·职场和发展·求职招聘·春招
实习僧企业版9 天前
如何为中小企业点亮校招吸引力的灯塔
大数据·春招·雇主品牌·招聘技巧·口碑
逻辑驱动的ken9 天前
Java高频面试考点场景题13
java·开发语言·jvm·面试·求职招聘·春招