2013-01-22 8 views
17

Czy w Javie jest biblioteka, która wykonuje następujące czynności? Numer thread powinien być wielokrotnie powtarzany przez 1 milisekundę, dopóki warunek nie zostanie spełniony lub osiągnięty zostanie maksymalny czas.Uśpij i sprawdź, czy warunek jest prawdziwy.

Ta sytuacja występuje najczęściej, gdy test czeka na spełnienie pewnych warunków. Na kondycję wpływa inny thread.

[EDYCJA] Tylko po to, aby było jaśniejsze, chcę, aby test trwał tylko X ms, zanim się nie powiedzie. Nie może czekać wiecznie, aż warunek stanie się prawdziwy. Dodaję wymyślony przykład.

class StateHolder{ 
    boolean active = false; 
    StateHolder(){ 
     new Thread(new Runnable(){ 
      public void run(){ 
       active = true; 
      } 
     }, "State-Changer").start() 
    } 
    boolean isActive(){ 
     return active; 
    } 
} 


class StateHolderTest{ 
    @Test 
    public void shouldTurnActive(){ 
     StateHolder holder = new StateHolder(); 
     assertTrue(holder.isActive); // i want this call not to fail 
    } 
} 
+0

Jeśli istnieje inny wątek, który je kontroluje, czy można ustawić faktyczny "gotowy" sygnał z oczekiwaniem/powiadomieniem zamiast odpytywaniem? – Thomas

+0

_ Na stan wpływa inny wątek_ Czy mogę powiedzieć, że mogę użyć _Thread join_ tutaj? W każdym razie +1 za pytanie. – Amarnath

Odpowiedz

0

Nie należy spać, sprawdzać, spać i sprawdzać. Chcesz poczekać na zmienną warunku i zmienić jej stan i obudzić wątek, gdy nadejdzie czas, aby coś zrobić.

+0

zobacz mój poprawiony opis. – Rag

15

EDIT

Większość odpowiedzi skupić się na niskim poziomie API z czeka i powiadamia lub warunkach (które działają mniej więcej w ten sam sposób): trudno jest uzyskać prawo jeśli nie są do tego przyzwyczajeni. Dowód: 2 z tych odpowiedzi nie używają poprawnie czekania.
java.util.concurrent oferuje interfejs API wysokiego poziomu, w którym wszystkie te zawiłości zostały ukryte.

IMHO, nie ma sensu używanie wzorca oczekiwania/powiadamiania, gdy istnieje wbudowana klasa w pakiecie jednoczesnym, która osiąga to samo.


CountDownLatch z początkowej liczby 1 robi dokładnie to:

  • Gdy warunek staje się prawdą, zadzwoń latch.countdown();
  • w swoim wątku oczekiwania, zastosowanie: boolean ok = latch.await(1, TimeUnit.SECONDS);

Konkretny przykład:

final CountDownLatch done = new CountDownLatch(1); 

new Thread(new Runnable() { 

    @Override 
    public void run() { 
     longProcessing(); 
     done.countDown(); 
    } 
}).start(); 

//in your waiting thread: 
boolean processingCompleteWithin1Second = done.await(1, TimeUnit.SECONDS); 

Uwaga: CountDownLatches są bezpieczne dla wątków.

+0

To jest naprawdę najlepsza odpowiedź, czyste i wydajne rozwiązanie ... –

-1

Wygląda na to, co chcesz zrobić jest sprawdzenie stanu, a następnie, jeśli jest fałszywe czekać aż timeout. Następnie w drugim wątku powiadom po zakończeniu operacji.

Kelner

synchronized(sharedObject){ 
    if(conditionIsFalse){ 
     sharedObject.wait(timeout); 
     if(conditionIsFalse){ //check if this was woken up by a notify or something else 
      //report some error 
     } 
     else{ 
      //do stuff when true 
     } 
    } 
    else{ 
     //do stuff when true 
    } 
} 

Changer

synchronized(sharedObject){ 
    //do stuff to change the condition 
    sharedObject.notifyAll(); 
} 

To powinno wystarczyć dla Ciebie. Możesz także zrobić to za pomocą blokady spin, ale musisz sprawdzić limit czasu za każdym razem, gdy przejdziesz przez pętlę. Kod może być jednak nieco prostszy.

+3

nie powinieneś używać czekania poza pętlą while. – assylias

1

Szukałem rozwiązania takiego jak to, co zapewnia Awaitility. Myślę, że wybrałem niewłaściwy przykład w moim pytaniu. Chodziło mi o sytuację, w której spodziewasz się wystąpienia zdarzenia asynchronicznego, które jest tworzone przez usługę innej firmy, a klient nie może zmodyfikować usługi, aby oferować powiadomienia. Bardziej rozsądnym przykładem może być ten poniżej.

class ThirdPartyService { 

    ThirdPartyService() { 
     new Thread() { 

      public void run() { 
       ServerSocket serverSocket = new ServerSocket(300); 
       Socket socket = serverSocket.accept(); 
       // ... handle socket ... 
      } 
     }.start(); 
    } 
} 

class ThirdPartyTest { 

    @Before 
    public void startThirdPartyService() { 
     new ThirdPartyService(); 
    } 

    @Test 
    public void assertThirdPartyServiceBecomesAvailableForService() { 
     Client client = new Client(); 
     Awaitility.await().atMost(50, SECONDS).untilCall(to(client).canConnectTo(300), equalTo(true)); 
    } 
} 

class Client { 

    public boolean canConnect(int port) { 
     try { 
      Socket socket = new Socket(port); 
      return true; 
     } catch (Exception e) { 
      return false; 
     } 
    } 
} 
+0

Precyzyjnie, "Condition" lub inne rozwiązania oparte na asynchroniczności w Javie kończą się plątaniem kodu i czytaniem naprawdę nienaturalnym dla przypadków z bibliotekami 3rd party, które nie dają żadnego punktu wejścia do synchronicznego oczekiwania na przetwarzanie w tle. W końcu użyłem 'awaitility' także w kodzie nie testującym, ponieważ jest to najlepiej przystosowane rozwiązanie w tym przypadku, szczególnie z punktu widzenia czytelności/konserwacji. – desseim

2

Awaitility zapewnia prosty i czysty roztwór.

oczekiwać() atmost (10 sekund), ważnych elementów.Dopóki (() -> stan());