40

有这个等待声明:

public final native void wait(long timeout) throws InterruptedException;

它可能会因 InterruptedException 或超时而退出,或者因为在另一个线程中调用了 Notify/NotifyAll 方法,异常很容易捕获但是......

有什么方法可以知道退出原因是超时还是通知?

编辑:

这是一种可行的棘手方法,(尽管我不喜欢它)

          long tBefore=System.currentTimeMillis();
          wait(TIMEOUT);
          if ((System.currentTimeMillis() - tBefore) > TIMEOUT) 
            { 
               //timeout
            }
4

7 回答 7

18

notify 可以返回的还有一个原因:虚假唤醒。这是一件不太可能但可能的事情,因为在某些硬件/操作系统组合上防止虚假唤醒非常昂贵。

因此,您始终必须在循环中调用 wait() 并重新检查您正在等待的条件。在这项工作期间,很容易同时检查超时。

有关详细信息,我推荐《Java Concurrency In Practice》一书。并使用更高级别的结构,这一切都对你正确。

于 2010-08-03T15:11:14.743 回答
14

除非您提供一些额外的代码,否则您无法区分两者。例如,通过添加一个ThreadLocal Boolean设置为true仅 onnotify()

但首先你必须确保你的逻辑需要这种区分。

于 2010-08-03T14:58:41.803 回答
14

这并不能完全回答问题,但它可能会解决您的问题:使用更高级别的并发机制。等待/通知通常比您想要的要低级,这也是许多其他原因。

例如,如果您正在使用BlockingQueue.poll(long, TimeUnit),您可以检查结果是否为 null 以了解您是否超时。

于 2010-08-03T15:01:03.560 回答
7

不使用System.currentTimeMillis()System.nanoTime()改为使用。

第一个测量绝对时间(基于系统时钟),如果系统时间改变,可能会产生奇怪的结果。例如:如果时钟向后移动一个小时,则 5 秒的等待可能会持续一个小时,或者如果时钟向前移动,则会在 0 秒后等待 10 分钟。

第二个测量相对时间。它总是以恒定的速度向一个方向运行,但它没有原点。这意味着这些值只能用于测量相对时间,但可以也不应该用于确定日期。

于 2014-12-16T14:05:10.340 回答
2

没有办法直接说明- 也就是说,您必须添加额外的代码来确定这一点。通常,当您 wait() 时,您正在等待以某种方式改变对象状态的事情发生——例如,可能通过设置一个布尔变量。如果是这种情况,那么您可以简单地检查该变量的状态以查看事件是否发生,或者您只是超时。或者您可以查看 System.currentTimeMillis() 的值以查看经过的时间是否大于或等于超时期限 - 如果是,那将是您可能超时的线索(尽管这不是绝对保证)。或者如果经过的时间小于超时时间,那么您肯定没有超时。这有帮助吗?

于 2010-08-03T15:22:33.110 回答
2

您应该使用不等待/通知方法。

使用带条件的锁会更好https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html#await-long-java.util.concurrent.TimeUnit-

它具有等待超时,如果在从方法返回之前可检测到等待时间已过,则返回 false,否则返回 true

于 2018-07-23T08:40:46.593 回答
1

通知和超时不会引发异常。

我认为最好依赖java.lang.concurrent包同步对象而不是使用Object.wait().

于 2010-08-03T14:59:07.513 回答