Pergunta

Estou tentando implementar Futuro.get (longa, timeunit) em termos de Timeunit.timedwait (objeto, longo).

Não está claro como usar TimeUnit.timedWait(Object, long) De uma maneira que lida com as altas espúrias sem perder o componente de nanossegundos da TimeUnunit. Normalmente você faria algo assim:

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);
  }
}

Mas você perde o componente nanossegundo. Se todos estão simplesmente soltando o componente de nanossegundos, então qual é o ponto de TimeUnit até mesmo apoiando nanossegundos e oferta TimeUnit.timedWait()?

Foi útil?

Solução 3

Countdownlatch parece ser a maneira mais fácil de implementar isso:

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 não é vulnerável a Avisões espúrias (porque pode verificar o estado de trava internamente antes de retornar).

Outras dicas

Antes de esperar, guarde o horário em que deseja tempo limite.

Antes de notificar o encadeamento de espera, defina um estado compartilhado (sincronizado), sinalizando que o thread de espera deve parar de esperar porque o cálculo está concluído.

Quando o seu tópico acorda da espera por qualquer motivo, ele deve verificar o estado compartilhado para ver se deve parar de esperar e também deve verificar quanto tempo resta antes que o tempo limite expire. Se o tempo limite não expirou, e o estado compartilhado diz não diz para parar de esperar, você deve esperar novamente (mas usando um novo tempo mais curto calculado a partir do horário atual).

A resposta para sua pergunta está no Object.wait (longo) especificação:

Um fio também pode acordar sem ser notificado, interrompido ou cronometrado, o chamado despertar espúrio. Embora isso raramente ocorra na prática, as aplicações devem se proteger, testando a condição que deveria ter causado a despertar o thread e continuar esperando se a condição não for satisfeita. Em outras palavras, as esperas sempre devem ocorrer em loops, como esta:

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

(Para obter mais informações sobre este tópico, consulte a Seção 3.2.3 na "programação simultânea de Doug Lea em Java (segunda edição)" (Addison-Wesley, 2000), ou item 50 no "Guia de Idiomas de Programação Java" de Joshua Bloch (Addison- Wesley, 2001).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top