【JUC】wait 和 sleep

wait() 和 sleep() 是 Java 多线程中两个非常容易混淆的方法,它们都会让当前线程暂停执行,但用途、机制和使用场景有本质区别。

核心区别一览

对比项 wait() sleep()
所属类 Object Thread
是否释放锁 释放持有的监视器锁 不释放锁
调用前提 必须在 synchronized 块/方法中 任意位置都可调用
唤醒方式 notify() / notifyAll() 或超时 超时自动醒,或 interrupt()
异常 抛 InterruptedException 抛 InterruptedException
主要用途 线程间通信/协作 单纯让线程暂停一段时间

一、wait()

wait() 定义在 java.lang.Object 上,因为它是**对象监视器(monitor)**机制的一部分。每个对象都有一把锁和一个等待队列。

关键特性

  1. 必须持有该对象的锁才能调用 ,否则抛 IllegalMonitorStateException。
  2. 调用后会释放锁,让其他线程有机会进入同步块。
  3. 线程进入该对象的 等待集(wait set) ,直到:
    • 其他线程调用同一对象的 notify() / notifyAll()
    • 或者等待时间超时(wait(long timeout))
  4. 被唤醒后不会立即执行 ,需要重新竞争锁,拿到锁后才从 wait() 返回。

标准用法(生产者-消费者模式)

java 复制代码
class Buffer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int CAPACITY = 5;

    public synchronized void produce(int value) throws InterruptedException {
        while (queue.size() == CAPACITY) {
            wait();  // 队列满,释放锁并等待
        }
        queue.add(value);
        notifyAll(); // 唤醒等待的消费者
    }

    public synchronized int consume() throws InterruptedException {
        while (queue.isEmpty()) {
            wait();  // 队列空,释放锁并等待
        }
        int value = queue.poll();
        notifyAll(); // 唤醒等待的生产者
        return value;
    }
}

为什么用 while 而不是 if?

线程被唤醒后可能条件又不满足了(虚假唤醒 spurious wakeup ,或别的线程抢先消费了),所以必须循环检查条件:

java 复制代码
while (条件不满足) {
    wait();
}

二、sleep()

sleep() 是 Thread 的静态方法,作用是让当前线程暂停指定时间。

关键特性

  1. 调用时不会释放任何锁------如果持有锁,其他线程照样进不来。
  2. 到时间后线程回到就绪状态,等待 CPU 调度。
  3. 可以被 interrupt() 打断,抛出 InterruptedException。
java 复制代码
Thread.sleep(1000);          // 睡 1 秒
Thread.sleep(1000, 500000);  // 1 秒 + 500 微秒(纳秒参数)

常见用途

  • 模拟耗时操作、轮询间隔
  • 让出 CPU 给其他线程
java 复制代码
while (true) {
    // 轮询检查任务
    if (hasTask()) {
        doTask();
    }
    Thread.sleep(500); // 每 500ms 检查一次
}

三、为什么 sleep 不释放锁?

看个例子就明白了:

java 复制代码
synchronized (lock) {
    Thread.sleep(5000); // 这 5 秒内,其他线程无法进入这个同步块
}

如果 sleep 释放了锁,同步语义就被破坏了------synchronized 保护的临界区可能被其他线程插入执行,逻辑就乱了。而 wait 的设计目的恰恰是主动让出锁去等别人做事,所以必须释放。


四、记忆口诀

  • wait 是"等通知":让出锁,等别人叫醒我 → 用于线程协作
  • sleep 是"打个盹":抱着锁睡,到点自己醒 → 用于延时

五、补充:现代替代方案

虽然 wait/notify 是基础,但实际开发中更推荐用 java.util.concurrent 包里的高级工具,它们更安全、更易读:

  • Condition(配合 ReentrantLock)------ 替代 wait/notify,支持多个等待队列
  • BlockingQueue ------ 直接解决生产者-消费者问题
  • LockSupport.park()/unpark() ------ 更底层的线程阻塞原语,Lock 的底层实现
java 复制代码
// 用 BlockingQueue 替代手写 wait/notify
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
queue.put(value);      // 满了自动阻塞
int v = queue.take();  // 空了自动阻塞
相关推荐
知守观2 小时前
18年老兵的十条开发军规:每条背后都是一个翻车现场
java·后端·程序员
kimnoic2 小时前
Python常见模块及其用法示例详解
开发语言·python
BBmmo2 小时前
我的 Java 学习笔记 · 第 11 篇:Condition.await 为什么必须放在 while 里
java
SimonKing2 小时前
一只离线鼠鼠,干翻了一堆在线格式转换网站
java·后端·程序员
程序员-Benothing2 小时前
Linux文本排序统计命令:sort、uniq、wc、cut、tr实战
java·开发语言·算法
clz13145212 小时前
双重检查锁 DCL 终极优化:懒加载 + 线程安全 + 高性能
java
CRMEB2 小时前
前后端技术栈全面换代!CRMEB 多商户(Java)v3.0更新预告
java·开发语言
开发者联盟league2 小时前
c++11 使用chrono库转换时间点和字符串,计算时间点间隔
java·c++·算法
我最爱吃鱼香茄子3 小时前
【毕业设计优选】人力资源管理系统|Java|SpringBoot|Vue|前后端分离|带详细文档+部署教学视频
java·vue·毕业设计·springboot