孤舟笔记 并发篇二十八 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 的危害。这种理解深度在排查死锁和性能问题时至关重要。

原文阅读


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

相关推荐
落魄江湖行21 天前
孤舟笔记 互联网常用框架篇一 Dubbo和Spring Cloud到底该选谁?优缺点对比一次讲透
春招·孤舟笔记·dubbo和spring cl
落魄江湖行21 天前
孤舟笔记 Spring全家桶篇二十六 简述Nacos配置更新的工作流程?配置变更是怎么通知到服务的
春招·孤舟笔记·简述nacos配置更新的工作流
落魄江湖行21 天前
孤舟笔记 互联网常用框架篇二 Dubbo服务请求失败怎么处理?集群容错策略你用过几种
春招·孤舟笔记
落魄江湖行21 天前
孤舟笔记 Spring全家桶篇二十五 谈谈EurekaServer数据同步原理?注册中心怎么保证数据一致性
春招·孤舟笔记·谈谈eurekaserver数
落魄江湖行21 天前
孤舟笔记 互联网常用框架篇三 Dubbo是如何动态感知服务下线的?注册中心和服务端双保险
春招·孤舟笔记·dubbo是如何动态感知服务下
落魄江湖行1 个月前
孤舟笔记 IO 与网络编程篇三 IO和NIO的区别是什么?从阻塞到非阻塞的范式革命
春招·孤舟笔记·io和nio的区别
落魄江湖行1 个月前
孤舟笔记 并发篇十八 为什么启动线程不能直接调用run()方法?调用两次start()又会怎样?这个设计藏着大智慧
thread·java并发·春招·孤舟笔记
逻辑驱动的ken1 个月前
Java高频面试考点场景题22
java·开发语言·jvm·面试·职场和发展·求职招聘·春招
落魄江湖行1 个月前
孤舟笔记 并发篇二十九 volatile关键字有什么用?它的实现原理是什么?面试必问的轻量级同步机制
java并发·春招·孤舟笔记·volatile关键字