如何区分等待(长时间超时)退出通知或超时?

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()

【讨论】:

以上是关于如何区分等待(长时间超时)退出通知或超时?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决高并发,连接等待超时的异常

如何在等待响应时增加 AWS Sagemaker 调用超时

任何区分取消和超时的方法

如何使用 Docker 使 phpmyadmin 持续更长时间而不会超时

如何区分 curl 最大时间和连接超时?

长时间 ASP.NET 操作的 IIS 请求超时