Domanda

Sto cercando di implementare noreferrer Future.get (lungo, TIMEUNIT) in termini di TimeUnit.timedWait (Object, lungo) .

Non è chiaro come utilizzare TimeUnit.timedWait(Object, long) in un modo che gestisce wakeups spuri senza perdere la componente nanosecondo di TIMEUNIT. Normalmente si farebbe qualcosa di simile:

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

, ma si perde la componente nanosecondo. Se ognuno è semplicemente caduta della componente nanosecondo allora qual è il punto di TimeUnit anche sostenere nanosecondi e offrendo TimeUnit.timedWait()?

È stato utile?

Soluzione 3

CountDownLatch sembra essere il modo più semplice per implementare questa:

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 non è vulnerabile a wakeups spuria (perché può verificare lo stato latch internamente prima di tornare).

Altri suggerimenti

Prima di aspettare, memorizzare l'ora in cui si desidera timeout.

Prima di notificare il thread in attesa, impostare uno stato condiviso (sincronizzato) segnalando che il thread in attesa dovrebbe smettere di aspettare perché il calcolo è completo.

Quando il filo si sveglia dal l'attesa per qualsiasi motivo si deve verificare lo stato condiviso per vedere se deve smettere di aspettare, e dovrebbe anche controllare quanto tempo è rimasto prima della scadenza del timeout. Se il timeout non è scaduto, e lo stato condiviso racconta non dire a smettere di aspettare allora si dovrebbe aspettare di nuovo (ma usando un nuovo tempo più breve fuori calcolata a partire dal momento attuale).

La risposta alla tua domanda sta nella Object.wait (lungo) spec:

Un thread può anche svegliare senza essere notificato, interrotto o timeout, un cosiddetto sveglia spuria. Anche se questo si verifica raramente in pratica, le applicazioni devono impedire che essa testando per la condizione che deve aver causato il filo di essere risvegliata, e continuare ad attendere se la condizione non è soddisfatta. In altre parole, attese dovrebbero sempre avvenire in loop, come questo:

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

(Per maggiori informazioni su questo argomento, vedere la sezione 3.2.3 in "programmazione concorrente in Java (Second Edition)" di Doug Lea (Addison-Wesley, 2000), o Articolo 50 in Joshua Bloch di "Effective Java Programming Language Guide" (Addison-Wesley, 2001).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top