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()
方法的典型应用场景。
开发建议
- 始终在循环中检查条件:避免因“虚假唤醒”导致逻辑错误。
- 合理选择通知方式:优先使用
notifyAll()
确保所有线程公平竞争。 - 设置超时机制:避免因外部因素导致的无限等待。
- 异常处理规范化:捕获
InterruptedException
并恢复线程中断状态。
进阶学习方向
- 探索
java.util.concurrent
包中的Condition
接口,对比wait-notify
模式与Condition
的优劣。 - 学习
ReentrantLock
和Semaphore
等更高级的同步工具。
通过本文的系统讲解,开发者可以掌握 Java Object wait() 方法
的核心原理与实践技巧,为构建高效、稳定的并发程序奠定基础。