2012-05-26 14 views
5

Ich verwende eine CountDownLatch für das Warten auf ein bestimmtes Ereignis von einer anderen Komponente (in einem anderen Thread ausgeführt). Die folgende Vorgehensweise würde die Semantik meiner Software passen, aber ich bin mir nicht sicher, ob es als ein Werk erwarte ich:Mehrere Aufrufe von CountDownLatch.await (int) mit Timeout

mCountDownLatch.await(3000, TimeUnit.MILLISECONDS) 
otherComponent.aStaticVolatileVariable = true; 
mCountDownLatch.await(3500, TimeUnit.MILLISECONDS); 
... <proceed with other stuff> 

Das Szenario folgende sein soll: Ich warte bis 3 Sekunden, und wenn der Riegel nicht auf 0 heruntergezählt, benachrichtige ich die andere Komponente mit dieser Variablen, und dann warte ich höchstens 3,5 Sekunden. Wenn es wieder eine Zeitüberschreitung gibt, ist es mir egal und wird mit anderen Operationen fortfahren.

Hinweis: Ich weiß, dass es nicht so aussieht, aber das obige Szenario ist absolut vernünftig und in meiner Software gültig.

Ich habe die Dokumentation von warten (int, TimeUnit) und CountDownLatch gelesen, aber ich bin kein Java/Android-Experte, also brauche ich eine Bestätigung. Für mich alle Szenarien gültig aussehen:

  • Wenn der erste await erfolgreich ist, dann warten auf die andere sofort zurück
  • Wenn die ersten await mal aus, dann die andere harren noch gültig ist; daher, wenn der andere Thread das statische Signal bemerkt, könnte das zweite await Rückkehr erfolgreich
  • Beide warten auf Anrufe Zeit aus (das ist in Ordnung nach Semantik meine Software)

Bin ich mit await (.. .) richtig? Kann eine Sekunde (...) auf die oben beschriebene Art und Weise verwendet werden, selbst wenn eine vorherige (...) auf dasselbe Objekt abläuft?

Antwort

4

Wenn ich Ihre Frage richtig verstehe, beweist dieser Test, dass alle Ihre Annahmen/Anforderungen wahr/erfüllt sind. (Lauf mit JUnit und hamcrest.) Beachten Sie den Code in das runCodeUnderTest() Verfahren, auch wenn es um den Faktor 10.

import org.junit.Before; 
import org.junit.Test; 

import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.TimeUnit; 

import static org.hamcrest.Matchers.closeTo; 
import static org.hamcrest.Matchers.lessThan; 
import static org.junit.Assert.assertThat; 

public class CountdownLatchTest { 
    static volatile boolean signal; 
    CountDownLatch latch = new CountDownLatch(1); 
    long elapsedTime; 
    long[] wakeupTimes = new long[2]; 

    @Before 
    public void setUp() throws Exception { 
     signal = false; 
    } 

    @Test 
    public void successfulCountDownDuringFirstAwait() throws Exception { 
     countDownAfter(150); 
     runCodeUnderTest(); 
     assertThat((double) elapsedTime, closeTo(150, 10)); 
     assertThat(wakeupTimeSeparation(), lessThan(10)); 
    } 

    @Test 
    public void successfulCountDownDuringSecondAwait() throws Exception { 
     countDownAfter(450); 
     runCodeUnderTest(); 
     assertThat((double) elapsedTime, closeTo(450, 10)); 
     assertThat((double) wakeupTimeSeparation(), closeTo(150, 10)); 
    } 

    @Test 
    public void neverCountDown() throws Exception { 
     runCodeUnderTest(); 
     assertThat((double) elapsedTime, closeTo(650, 10)); 
     assertThat((double) wakeupTimeSeparation(), closeTo(350, 10)); 
    } 

    @Test 
    public void countDownAfterSecondTimeout() throws Exception { 
     countDownAfter(1000); 
     runCodeUnderTest(); 
     assertThat((double) elapsedTime, closeTo(650, 10)); 
     assertThat((double) wakeupTimeSeparation(), closeTo(350, 10)); 
    } 

    @Test 
    public void successfulCountDownFromSignalField() throws Exception { 
     countDownAfterSignal(); 
     runCodeUnderTest(); 
     assertThat((double) elapsedTime, closeTo(300, 10)); 
    } 

    private int wakeupTimeSeparation() { 
     return (int) (wakeupTimes[1] - wakeupTimes[0]); 
    } 

    private void runCodeUnderTest() throws InterruptedException { 
     long start = System.currentTimeMillis(); 
     latch.await(300, TimeUnit.MILLISECONDS); 
     wakeupTimes[0] = System.currentTimeMillis(); 
     signal = true; 
     latch.await(350, TimeUnit.MILLISECONDS); 
     wakeupTimes[1] = System.currentTimeMillis(); 
     elapsedTime = wakeupTimes[1] - start; 
    } 

    private void countDownAfter(final long millis) throws InterruptedException { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       sleep(millis); 
       latch.countDown(); 
      } 
     }).start(); 
    } 

    private void countDownAfterSignal() { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       boolean trying = true; 
       while (trying) { 
        if (signal) { 
         latch.countDown(); 
         trying = false; 
        } 
        sleep(5); 
       } 
      } 
     }).start(); 
    } 

    private void sleep(long millis) { 
     try { 
      Thread.sleep(millis); 
     } catch (InterruptedException e) { 
      throw new IllegalStateException("Unexpected interrupt", e); 
     } 
    } 
} 
+0

Vielen Dank für die sehr gründliche Code mit Zeiterfassung und die Timeouts reduziert setzt ist. Eigentlich war meine Frage viel einfacher, aber wenn ich richtig liege, hast du tatsächlich bewiesen, dass meine Annahmen richtig sind, also herzlichen Dank dafür! Meine Kernfrage war nur diese: Wenn die erste Wartezeit() ** für ein Objekt abläuft, kann ich dann auf dasselbe Objekt (wie in meinem Code) warten()? Ich denke ja, aber wie gesagt, ich bin kein Java-Experte. (Nachdem ich die endgültige Bestätigung erhalten habe, werde ich deine Antwort als Antwort markieren, denn sie ist mehr als das, was ich brauchte.) –

+1

Heh, um ehrlich zu sein, ich war mir der Antwort nicht hundertprozentig sicher mein Kopf, und der beste Weg, eine Antwort zu finden, ist es zu versuchen. Daher der Test :) –