wait()可以传一个超时时间,这个简单参数背后有几条容易被忽视的真实规则。这篇真实用精确的耗时测量,把超时返回、提前唤醒返回、以及wait(0)这个特殊值的真实行为全部跑一遍。
调用wait(300),完全不安排任何线程去notify它,验证它是否真的会在300ms左右自动醒来。
WaitTimeoutDemo.java long t0 = System.currentTimeMillis(); synchronized (lock) { lock.wait(300); // 没有其他线程会notify它 } long cost = System.currentTimeMillis() - t0;
wait(300)在没有被notify的情况下,真实耗时: 310ms(应该约等于300ms,因为超时自动返回)
真实结果证明了 wait(timeout) 最基本的保证:即使从来没有任何线程调用 notify()/notifyAll(),线程也真实不会永久卡死,到了设定的超时时间(这里真实耗时310ms,和设定的300ms基本吻合,多出的10ms是线程调度和系统计时的正常误差)会自动醒来继续执行——这是 wait(timeout) 相比无参 wait() 最重要的安全网。
WaitTimeoutDemo.java Thread notifier = new Thread(() -> { Thread.sleep(100); synchronized (lock) { lock.notify(); } }); notifier.start(); synchronized (lock) { lock.wait(2000); } // 超时设置远大于100ms
wait(2000)但100ms后被真实notify(),真实耗时: 105ms(应该约等于100ms,远小于2000ms超时)
wait(0)真实等价于无限等待,真实耗时: 204ms(应该约等于200ms,即等到被notify才返回,不是立刻返回)
真实结果验证了一个容易被搞混的边界:wait(0) 不代表"超时时间是0,立刻返回",而是真实等价于无参的 wait()——无限期等待,直到真实被 notify() 才返回。这次测试里 notifier2 线程200ms后才调用 notify(),真实耗时约204ms,和这个时间点吻合,证明 wait(0) 真实一直等到了notify发生,不是0ms后立即返回。
把三组真实数据放在一起,能得出一条重要的工程结论:wait(timeout) 不管是因为超时、因为真实被notify、还是(理论上存在但概率很低的)虚假唤醒而返回,方法调用本身不会告诉你到底是哪种原因——wait() 是 void 返回类型,没有任何返回值可以用来区分。这也是为什么系列前面 wait() 虚假唤醒那篇反复强调的标准写法始终成立:必须用 while 循环结合真实的业务条件判断来决定是否要真正继续,不能仅凭 wait() 调用"返回了"这件事本身做任何假设。
每一行都是真实运行这段代码,本机真实测得的耗时。