如何区分wait(long timeout)是因为notify而退出还是因为超时?

42

有了这个等待声明:

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

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

有没有办法知道退出的原因是超时还是通知?

编辑:

这是一种可能会起作用的棘手方式(尽管我不喜欢它)

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

3
我有类似的需求,结果发现 Semaphore 更适合;Semaphore.tryAcquire(long timeout, TimeUnit unit) 如果超时了会返回 false - Santosh
Semaphore可以工作,除非你需要等待的能力,这是Semaphore公共合同不支持的。 - Victor Grazi
需要将条件更改为 (>=):if ((System.currentTimeMillis() - tBefore) >= TIMEOUT) - Filomat
1
你的解决方案不正确。在通知事件之后,调度程序会在没有运行更高优先级线程的情况下恢复线程。这意味着,在通知之后,会出现一段时间间隔,然后才会恢复线程。如果这段时间足够长以使您的条件成立,则您的代码将处理超时而不是正确的行为。 - Rudy Barbieri
7个回答

18

还有一个可能导致notify()方法返回的原因是虚假唤醒。这很少见但是确实可能发生,因为在某些硬件/操作系统组合中防止虚假唤醒非常昂贵。

因此,在使用wait()方法时,您始终需要在循环中调用并重新检查您正在等待的条件。在此过程中,同时检查超时也很容易。

对于详细信息,我建议阅读《Java并发实践》一书,并使用可以正确处理所有这些问题的更高级别构造。


14

虽然这并不直接回答你的问题,但它可能会解决你的问题: 使用更高级别的并发机制。 等待/通知通常比你想要的更低级别,因为这是其中许多原因之一。

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


13

除非您提供一些额外的代码,否则您无法区分这两个。例如,通过添加一个 ThreadLocal 布尔值,仅在 notify() 上设置为 true

但首先,您必须确保您的逻辑需要进行这种区分。


6
OP已经知道不会抛出异常,并希望区分超时和通知。但这并没有提供任何有用的信息来实现这一点。 - Mark Peters
10
在调用notify()时,我不明白将本地线程布尔值设置为true会如何有助于解决问题。调用notify()的线程与从wait()中唤醒的线程不会是同一个线程。 - Per Mildner
2
你的意思是一个简单的布尔字段,我想。不是ThreadLocal。但是简单的布尔字段可以帮助。它甚至不需要是volatile,因为wait/notify将从synchronized块中调用。只需在notify()之前将其设置为true,并在wait()之后检查/重置即可。 - Ruslan Stelmachenko

7
不要使用System.currentTimeMillis(),改用System.nanoTime()。第一个方法测量绝对时间(基于系统时钟),如果更改系统时间,则结果可能很奇怪。例如:如果时钟向后移动一个小时,则5秒的等待可能会持续一个小时,或者如果时钟向前移动,则10分钟的等待将在0秒后完成。第二种方法测量相对时间,它总是以恒定速度单向运行,但它没有起点。这意味着这些值只能用于测量相对时间,不应该用于确定日期。

+1 鼓励人们停止编写在系统时钟更新时会出错的代码。但你没有提到应始终检查等待的原因是否仍然有效。 - SensorSmith

2

无法直接确定 - 也就是说,您需要添加其他代码来确定这一点。通常当您等待时(wait()),您正在等待某些事情发生,这会以某种方式更改对象的状态 - 例如通过设置布尔变量。如果是这种情况,则可以简单地检查该变量的状态以查看事件是否发生,或者您只是超时了。或者您可以查看System.currentTimeMillis()的值,以查看经过的时间是否大于或等于超时期限 - 如果是,则表明您可能已超时(尽管这并不是绝对保证)。如果经过的时间少于超时期限,则肯定没有超时。这有帮助吗?


@Hernán Eche,正如我在我的答案中所提到的,这并不是绝对的保证。如果您能告诉我为什么要确定它是否超时?我们可以寻找一些解决方案。 - YoK

2

1

在通知和超时时不会抛出异常。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接