Java Object wait(long timeout, int nanos) 方法(保姆级教程)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 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 Object wait(long timeout, int nanos)
方法作为 Object
类提供的关键方法之一,为开发者提供了精确控制线程等待时间的能力。无论是构建生产者-消费者模型,还是实现定时任务调度,这一方法都扮演着重要角色。本文将从基础概念出发,结合代码示例和实际场景,深入解析该方法的原理、用法及注意事项,帮助读者系统掌握这一技术点。
方法概述与核心作用
线程等待机制的核心
在 Java 中,线程可以通过 Object
类的 wait()
、notify()
和 notifyAll()
方法实现线程间通信。其中,wait()
方法使当前线程进入等待状态,直到被其他线程唤醒或超时。而 wait(long timeout, int nanos)
是 wait()
方法的重载版本,允许开发者以毫秒级精度控制等待时间。
方法签名:
public final void wait(long timeout, int nanos) throws InterruptedException
方法作用的比喻
可以将线程比作排队的人:
- wait():当前线程暂时离开队列,进入“休息室”等待。
- notify() 或 notifyAll():相当于向休息室发送通知,唤醒特定线程或全部线程。
- wait(timeout, nanos):设定一个“截止时间”,若在指定时间内未收到通知,则自动返回队列继续执行。
参数详解与底层逻辑
参数含义与约束
该方法接受两个参数:
- timeout (long):等待的毫秒数(精确到秒)。
- nanos (int):毫秒之外的纳秒部分(0-999,999,999)。
关键约束:
- 纳秒参数必须在 0 到 999,999,999 之间,超出范围会抛出
IllegalArgumentException
。 - 必须在同步块(
synchronized
)中调用,否则会抛出IllegalMonitorStateException
。
时间计算逻辑
方法的总等待时间为:
总时间 = timeout 毫秒 + (nanos 纳秒 / 1,000,000,000)
例如,wait(1, 500_000_000)
表示等待 1.5 秒。
使用场景与核心流程
典型应用场景
- 生产者-消费者模式:消费者线程在缓冲区为空时等待,直到生产者放入数据。
- 定时任务:线程在未收到信号时,按固定间隔检查任务状态。
- 资源竞争控制:避免线程频繁轮询共享资源,降低 CPU 占用。
方法调用的规范流程
使用 wait(timeout, nanos)
需遵循以下步骤:
- 获取对象锁:通过
synchronized
关键字锁定目标对象。 - 检查条件:在同步块内判断是否需要等待(如资源不足)。
- 调用 wait():若需等待,调用
wait(timeout, nanos)
进入等待状态。 - 响应唤醒或超时:等待结束后重新检查条件并执行后续操作。
代码示例与实践分析
示例 1:基础用法
public class WaitExample {
private static final Object lock = new Object();
private static boolean condition = false;
public static void main(String[] args) {
Thread waiter = new Thread(() -> {
synchronized (lock) {
System.out.println("等待开始");
try {
lock.wait(2, 0); // 等待 2 秒或被唤醒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("等待被中断");
} finally {
System.out.println("等待结束");
}
}
});
waiter.start();
try {
Thread.sleep(1000); // 主线程延迟 1 秒
synchronized (lock) {
lock.notify(); // 提前唤醒等待线程
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:
等待开始
等待结束
分析:主线程在 1 秒后调用 notify()
,提前唤醒等待线程,因此实际等待时间不足 2 秒。
示例 2:生产者-消费者模型
public class ProducerConsumerExample {
private static final Object lock = new Object();
private static int count = 0;
private static final int CAPACITY = 10;
public static void main(String[] args) {
Thread producer = new Thread(() -> {
while (true) {
synchronized (lock) {
while (count >= CAPACITY) {
try {
lock.wait(); // 缓冲区满时等待
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
count++;
System.out.println("生产者生产,当前库存:" + count);
lock.notify(); // 唤醒消费者
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread consumer = new Thread(() -> {
while (true) {
synchronized (lock) {
while (count <= 0) {
try {
lock.wait(3, 0); // 等待 3 秒后自动检查条件
if (count <= 0) {
System.out.println("消费者超时,放弃消费");
return; // 超时后终止线程
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
count--;
System.out.println("消费者消费,当前库存:" + count);
lock.notify(); // 唤醒生产者
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
consumer.start();
}
}
关键点解析:
- 超时机制:消费者线程在缓冲区为空时,最多等待 3 秒。若超时未被唤醒,则自动退出。
- 条件检查:使用
while
循环而非if
,避免虚假唤醒(False Wakeup)问题。
常见问题与注意事项
问题 1:为何必须在同步块中调用?
wait()
系列方法依赖对象锁(Monitor)。调用线程必须持有该对象的锁,否则无法安全释放锁并进入等待状态。
问题 2:如何避免死锁?
- 正确释放锁:确保
wait()
调用在synchronized
块内,且在调用后不会意外释放锁。 - 合理唤醒顺序:避免多个线程互相等待对方释放资源。
问题 3:wait(timeout)
与 wait(timeout, nanos)
的区别
wait(long timeout)
的纳秒部分默认为 0,而 wait(timeout, nanos)
允许更精确的纳秒级控制。
性能优化与替代方案
方法的局限性
- 精度限制:纳秒参数的精度受操作系统调度影响,实际精度可能低于纳秒级别。
- 中断风险:若线程在等待期间被中断,会抛出
InterruptedException
,需妥善处理。
替代方案:Thread.sleep()
Thread.sleep()
是静态方法,使当前线程休眠指定时间,但无法响应其他线程的唤醒信号。
对比表格:
| 方法 | 是否响应唤醒信号 | 是否需要持有锁 | 精度控制 |
|-----------------------|------------------|----------------|-------------------|
| wait(timeout, nanos)
| 是 | 是 | 毫秒+纳秒 |
| Thread.sleep()
| 否 | 否 | 毫秒(整数) |
结论
通过本文的讲解,读者应能理解 Java Object wait(long timeout, int nanos)
方法的核心逻辑、使用场景及潜在风险。这一方法是构建高效多线程应用的关键工具,但需结合同步机制、异常处理和代码结构设计,才能发挥其最大价值。在实际开发中,建议结合 Condition
接口(如 java.util.concurrent.locks
包)实现更灵活的线程协作,同时持续关注线程安全与性能优化。
掌握这一方法后,开发者可以更自信地应对复杂并发场景,例如分布式任务调度、实时数据处理等,为构建高并发系统打下坚实基础。