3

我正在尝试根据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()何在?

4

3 回答 3

3

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

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

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

于 2009-12-29T17:06:34.513 回答
1

您的问题的答案在于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 编程语言指南”(Addison-韦斯利,2001)。

于 2009-12-30T00:36:03.023 回答
1

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 不易受到虚假唤醒的影响(因为它可以在返回之前在内部检查锁存状态)。

于 2009-12-30T15:04:42.580 回答