Как использовать TimeUnit.timedWait() без потери наносекундной точности?

StackOverflow https://stackoverflow.com/questions/1975620

  •  21-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь реализовать Future.get(долго, временная единица) с точки зрения TimeUnit.timedWait(Объект, длинный).

Непонятно, как использовать 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()?

Это было полезно?

Решение 3

Обратный отсчет времени кажется, это самый простой способ реализовать это:

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 не подвержен ложные пробуждения (потому что он может внутренне проверить состояние защелки перед возвратом).

Другие советы

Прежде чем ждать, запишите время, в которое вы хотите установить тайм-аут.

Прежде чем уведомлять ожидающий поток, установите некоторое общее (синхронизированное) состояние, сигнализирующее о том, что ожидающий поток должен прекратить ожидание, поскольку вычисление завершено.

Когда ваш поток по какой-либо причине выходит из режима ожидания, он должен проверить общее состояние, чтобы увидеть, следует ли ему прекратить ожидание, и он также должен проверить, сколько времени осталось до истечения тайм-аута.Если тайм-аут не истек, и общее состояние сообщает, что не нужно прекращать ожидание, тогда вам следует подождать еще раз (но используя новый более короткий тайм-аут, рассчитанный исходя из текущего времени).

Ответ на ваш вопрос заключается в Объект. ждать (долго) спецификация:

Поток также может просыпаться без уведомления, прерывания или тайм-аута, так называемого ложного пробуждения.Хотя на практике это происходит редко, приложения должны защищаться от этого, проверяя условие, которое должно было вызвать пробуждение потока, и продолжая ждать, если условие не выполняется.Другими словами, ожидание всегда должно происходить в циклах, подобных этому:

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

(Для получения дополнительной информации по этой теме смотрите Раздел 3.2.3 в книге Дуга Ли "Параллельное программирование на Java (второе издание)" (Addison-Wesley, 2000) или пункт 50 в книге Джошуа Блоха "Эффективное руководство по языку программирования Java" (Addison-Wesley, 2001).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top