Java Object wait() 方法(长文讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

在 Java 编程中,多线程技术是实现高效并发处理的核心工具。当多个线程需要访问共享资源时,线程同步机制就显得尤为重要。Java 提供了 synchronized 关键字和 Object 类中的 wait()notify()notifyAll() 方法,共同构建了线程间协作的基础框架。本文将深入讲解 Java Object wait() 方法,并通过实际案例帮助读者理解其应用场景与使用规范。


二级标题:wait() 方法的核心概念与语法

什么是 wait() 方法?

wait()java.lang.Object 类中的一个原生方法,用于让当前线程暂停执行,并释放其持有的对象锁。简单来说,当线程调用 wait() 后,会进入“等待状态”,直到其他线程通过 notify()notifyAll() 唤醒它。

形象比喻

可以将线程比作在办公室工作的员工,而对象锁则是办公室的门锁。当员工(线程)进入办公室(获取对象锁)后,如果发现需要等待某个条件(如文件未到达),就会调用 wait(),相当于在办公室里坐下休息,并将门锁交给其他人使用。此时其他线程才能进入办公室(获取锁)继续工作。

语法与调用规则

wait() 方法的语法如下:

public final void wait() throws InterruptedException

调用前提

  • 必须在 synchronized 同步块或同步方法中调用,否则会抛出 IllegalMonitorStateException 异常。
  • 调用对象必须是当前线程持有的锁对象(即 this 或其他特定对象)。

相关方法对比

方法名功能描述
wait()让当前线程等待,直到被 notify()notifyAll() 唤醒,或因超时自动恢复
notify()唤醒一个等待该对象锁的线程
notifyAll()唤醒所有等待该对象锁的线程

二级标题:wait() 方法的典型应用场景

场景一:生产者-消费者问题

生产者-消费者模式是并发编程的经典案例。假设有一个共享缓冲区,生产者线程负责生产数据并放入缓冲区,消费者线程负责从缓冲区取出数据处理。当缓冲区满时,生产者需要等待;当缓冲区空时,消费者需要等待。

实际代码示例

class Buffer {
    private final int[] items = new int[10];
    private int count = 0;
    
    public synchronized void produce(int item) {
        while (count == items.length) {
            try {
                wait(); // 缓冲区满时生产者等待
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        items[count++] = item;
        notifyAll(); // 通知消费者可以消费
    }
    
    public synchronized int consume() {
        while (count == 0) {
            try {
                wait(); // 缓冲区空时消费者等待
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        int item = items[--count];
        notifyAll(); // 通知生产者可以生产
        return item;
    }
}

场景二:线程间条件等待

当线程需要等待某个条件满足时(如数据库连接可用、网络响应到达),wait() 可与 while 循环结合使用,确保在被唤醒时条件确实成立。


二级标题:使用 wait() 方法的注意事项

注意事项 1:必须在同步块中调用

// 错误示例:未在 synchronized 块中调用
public void wrongUsage() {
    Object lock = new Object();
    lock.wait(); // 抛出 IllegalMonitorStateException
}

注意事项 2:避免无限等待

若未正确实现 notify() 唤醒逻辑,线程可能永久处于等待状态。应通过 wait(long timeout) 设置超时时间:

try {
    wait(3000); // 最多等待 3 秒
} catch (InterruptedException e) {
    // 处理中断
}

注意事项 3:notify()notifyAll() 的选择

  • notify() 随机唤醒一个等待线程,可能引发“虚假唤醒”问题。
  • notifyAll() 唤醒所有线程,由 JVM 决定哪个线程获得锁。在资源竞争场景下更安全。

二级标题:常见问题与解决方案

问题 1:线程未被唤醒

可能原因

  • 调用 notify() 的对象与调用 wait() 的对象不一致。
  • notify() 调用发生在非同步块中。

解决方案

确保 notify()wait() 在同一对象的同步块中调用:

synchronized (lockObject) {
    lockObject.notify();
}

问题 2:线程饥饿现象

当频繁调用 notify() 但未合理分配资源时,可能导致某些线程长期无法获取锁。

解决方案

使用 Thread.sleep() 控制唤醒节奏,或采用 ReentrantLock + Condition 的更精细控制。

问题 3:异常处理

wait() 方法抛出 InterruptedException,需在代码中捕获并处理线程中断状态。

示例代码

try {
    wait();
} catch (InterruptedException e) {
    // 恢复中断状态
    Thread.currentThread().interrupt();
    // 根据业务逻辑选择终止或重试
}

二级标题:wait() 方法的进阶用法

使用 wait(long timeout) 实现超时等待

通过指定超时时间,线程在等待期间可自动恢复:

try {
    wait(2000); // 最多等待 2 秒
} catch (InterruptedException e) {
    // 处理中断
}

结合 while 循环的条件检查

避免“虚假唤醒”问题,需在 wait() 前后添加条件判断:

while (conditionNotMet()) {
    try {
        wait();
    } catch (InterruptedException e) {
        // 处理中断
    }
}

二级标题:总结与最佳实践

核心知识点回顾

  • wait() 方法必须在同步块中调用,且作用于当前线程持有的对象锁。
  • notify()notifyAll() 是唤醒等待线程的关键操作。
  • 生产者-消费者模式是 wait() 方法的典型应用场景。

开发建议

  1. 始终在循环中检查条件:避免因“虚假唤醒”导致逻辑错误。
  2. 合理选择通知方式:优先使用 notifyAll() 确保所有线程公平竞争。
  3. 设置超时机制:避免因外部因素导致的无限等待。
  4. 异常处理规范化:捕获 InterruptedException 并恢复线程中断状态。

进阶学习方向

  • 探索 java.util.concurrent 包中的 Condition 接口,对比 wait-notify 模式与 Condition 的优劣。
  • 学习 ReentrantLockSemaphore 等更高级的同步工具。

通过本文的系统讲解,开发者可以掌握 Java Object wait() 方法 的核心原理与实践技巧,为构建高效、稳定的并发程序奠定基础。

最新发布