如何区分等待(长时间超时)退出通知或超时?
Posted
技术标签:
【中文标题】如何区分等待(长时间超时)退出通知或超时?【英文标题】:How to differentiate when wait(long timeout) exit for notify or timeout? 【发布时间】:2011-03-24 18:53:10 【问题描述】:有这个等待声明:
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
【问题讨论】:
我也有类似的需求,在我的情况下,Semaphore
更适合;如果超时已过,Semaphore.tryAcquire(long timeout, TimeUnit unit)
返回false
。
Semaphore 可以工作,除非您需要有等待的能力,这不受 Semaphore 公共合约的支持。
需要将条件改为(>=):if ((System.currentTimeMillis() - tBefore) >= TIMEOUT)
您的解决方案不正确。在通知事件之后,调度程序会在其他具有更高优先级的线程没有运行时恢复线程。这意味着,在通知之后,在线程恢复之前会出现一个时间间隔,如果它足以满足您的条件,您的代码会管理超时而不是正确的行为。
【参考方案1】:
notify 可以返回的另一个原因是:虚假唤醒。这是不太可能但可能发生的事情,因为在某些硬件/操作系统组合上防止虚假唤醒非常昂贵。
因此,您始终必须在循环中调用 wait() 并重新检查您正在等待的条件。在这项工作期间,很容易同时检查超时。
有关详细信息,我推荐《Java 并发实践》一书。并使用更高级别的结构来为您解决所有问题。
【讨论】:
【参考方案2】:除非您提供一些额外的代码,否则您无法区分这两者。例如,通过添加一个 ThreadLocal
Boolean
仅在 notify()
上设置为 true
但首先你必须确保你的逻辑需要这种差异化。
【讨论】:
OP 已经知道不会抛出异常,并且想要区分超时和通知。这并没有为此提供任何有用的信息。 我看不出在执行 notify() 时将 thread-local 布尔值设置为 true 有何帮助。调用 notify() 的线程不会是从 wait() 唤醒的线程。 你的意思是简单的boolean
字段,我想。不是ThreadLocal
。但简单的布尔字段可能会有所帮助。它甚至不需要是volatile
,因为无论如何都会从synchronized
块调用wait/notify。只需在notify()
之前将其设置为true
并在wait()
之后检查/重置它。【参考方案3】:
这并不能完全回答问题,但它可能会解决您的问题:使用更高级别的并发机制。等待/通知的级别通常比您想要的要低,这也是许多其他原因。
例如,如果您使用的是BlockingQueue.poll(long, TimeUnit)
,您可以检查结果是否为 null 以了解您是否超时。
【讨论】:
【参考方案4】:不要使用System.currentTimeMillis()
,而是使用System.nanoTime()
。
第一个测量绝对时间(基于系统时钟),如果系统时间更改,可能会产生奇怪的结果。例如:如果时钟向后移动一个小时,则 5 秒的等待可能会持续一个小时,如果时钟向前移动,则会在 0 秒后等待 10 分钟。
第二个测量相对时间。它总是以恒定的速度向一个方向运行,但它没有原点。这意味着这些值只能用于测量相对时间,但可以也不应该用于确定日期。
【讨论】:
+1 鼓励人们停止编写因系统时钟更新而中断的代码。但是您没有提到应该始终检查等待的原因是否仍然有效。【参考方案5】:无法直接判断 - 也就是说,您必须添加额外的代码来确定这一点。通常,当您等待()时,您正在等待以某种方式改变对象状态的事情发生 - 例如。通过设置一个布尔变量,也许。如果是这种情况,那么您可以简单地检查该变量的状态以查看事件是否发生,或者您只是超时。或者您可以查看 System.currentTimeMillis() 的值以查看经过的时间是否大于或等于超时期限 - 如果是,那将是您可能超时的线索(尽管这不是绝对保证)。或者如果经过的时间小于超时时间,那么你肯定没有超时。这有帮助吗?
【讨论】:
@Hernán Eche 正如我在回答中所说,这不是绝对的保证。如果你能说出,你为什么要识别它是否超时?我们可以寻找一些解决方案。【参考方案6】:您应该使用不等待/通知的方法。
使用带条件的锁会更好https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html#await-long-java.util.concurrent.TimeUnit-
它具有等待超时,如果在从方法返回之前可检测到等待时间过去,则返回 false,否则返回 true
【讨论】:
【参考方案7】:在通知和超时时不会抛出异常。
我认为最好依赖java.lang.concurrent
包同步对象而不是使用Object.wait()
。
【讨论】:
以上是关于如何区分等待(长时间超时)退出通知或超时?的主要内容,如果未能解决你的问题,请参考以下文章