【问题标题】:How to use TimeUnit.timedWait() without losing nanosecond precision?如何在不损失纳秒精度的情况下使用 TimeUnit.timedWait()?
【发布时间】:2009-12-29 16:51:36
【问题描述】:

我正在尝试根据TimeUnit.timedWait(Object, long) 来实现Future.get(long, TimeUnit)

目前尚不清楚如何使用TimeUnit.timedWait(Object, long) 来处理虚假唤醒而不会丢失 TimeUnit 的纳秒组件。通常你会做这样的事情:

public V get(long timeout, TimeUnit unit)
{
  long expirationTime = new Date().getTime() + unit.toMillis(timeout);
  while (!condition)
  {
    long timeLeft = expirationTime - new Date().getTime();
    if (timeLeft <= 0)
      throw new TimeoutException();
    unit.timedWait(object, timeLeft);
  }
}

但是您丢失了纳秒部分。如果每个人都只是放弃纳秒组件,那么TimeUnit 甚至支持纳秒并提供TimeUnit.timedWait() 的意义何在?

【问题讨论】:

  • 您能否说明您担心虚假唤醒的情况?代码中是否有某些内容难以明确谁在控制对象监视器?
  • 我很确定谁在控制对象监视器。虚假唤醒是指即使没有人调用 notify() 或发生超时,wait() 操作也可能返回。

标签: java concurrency time-wait


【解决方案1】:

在等待之前,存储您想要超时的时间。

在通知等待线程之前,设置一些共享(同步)状态信号,指示等待线程应该停止等待,因为计算已完成。

当您的线程因任何原因从等待中唤醒时,它应该检查共享状态以查看它是否应该停止等待,并且它还应该检查在超时到期之前还剩下多长时间。如果超时没有过期,并且共享状态没有说停止等待,那么您应该再次等待(但使用从当前时间计算的新的更短的超时)。

【讨论】:

  • 不清楚如何存储你想要超时的时间而不丢失TimeUnit的纳秒部分。如果每个人都只是放弃纳秒组件,那么 TimeUnit 甚至支持纳秒并提供 TimeUnit.timedWait() 的意义何在?
  • 好吧,我真的不能说他们为什么选择允许纳秒精度。实际上,我怀疑您是否会从普通操作系统中获得这种精度。如果您确实需要这种精度,那么使用垃圾收集语言听起来是个非常糟糕的主意。
  • 我已经更新了这个问题,使其更加以 TimeUnit 为中心,但我已经给你一个很棒的 Object.wait() 建议投票。
【解决方案2】:

您的问题的答案在于Object.wait(long) 规范:

线程也可以在没有被通知、中断或超时的情况下唤醒,即所谓的虚假唤醒。虽然这在实践中很少发生,但应用程序必须通过测试应该导致线程被唤醒的条件来防范它,如果条件不满足则继续等待。换句话说,等待应该总是在循环中发生,就像这样:

 synchronized (obj) {
     while (<condition does not hold>)
         obj.wait(timeout);
     ... // Perform action appropriate to condition
 }

(有关此主题的更多信息,请参阅 Doug Lea 的“Java 中的并发编程(第二版)”(Addison-Wesley,2000 年)中的第 3.2.3 节,或 Joshua Bloch 的“有效的 Java 编程语言指南”中的第 50 条(艾迪生-韦斯利,2001)。

【讨论】:

  • 您需要记住在第二次等待时减少超时,因为有些时间已经过去了。
【解决方案3】:

CountDownLatch 似乎是实现这一点的最简单方法:

public class MutableFuture<T> implements Future<T>
{
  private final CountDownLatch done = new CountDownLatch(1);
  private T value;
  private Throwable throwable;

  public synchronized boolean isDone()
  {
    return done.getCount() == 0;
  }

  public synchronized T get(long timeout, TimeUnit unit)
    throws InterruptedException, ExecutionException, TimeoutException
  {
    if (!done.await(timeout, unit))
      throw new TimeoutException();
    if (throwable != null)
      throw new ExecutionException(throwable);
    return value;
  }

  // remaining methods left as exercise to the reader :)
}

CountdownLatch 不易受到spurious wakeups 的攻击(因为它可以在返回之前在内部检查闩锁状态)。

【讨论】:

    猜你喜欢
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 2019-10-08
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 2013-12-07
    • 1970-01-01
    相关资源
    最近更新 更多