2013-01-22 17 views
17

Esiste una libreria in Java che esegue quanto segue? A thread deve ripetutamente sleep per x millisecondi finché una condizione non diventa vera o viene raggiunto il tempo massimo.Sospendi e verifica fino a quando la condizione è vera

Questa situazione si verifica soprattutto quando un test attende che alcune condizioni diventino vere. La condizione è influenzata da un altro thread.

[EDIT] Solo per rendere più chiaro, voglio che il test attenda solo X ms prima che fallisca. Non può aspettare per sempre che una condizione diventi realtà. Sto aggiungendo un esempio forzato.

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

Se c'è un altro thread che lo controlla, è possibile impostare un segnale effettivo 'fatto' con wait/notify al posto del polling? – Thomas

+0

_La condizione è influenzata da un'altra discussione_ Posso dire che posso usare _Thread join_ qui? Ad ogni modo +1 per la domanda. – Amarnath

risposta

0

Non si dovrebbe dormire e controllare, dormire e controllare. Si desidera attendere una variabile di condizione e cambiare la condizione e svegliare il thread quando è il momento di fare qualcosa.

+0

per favore vedi la mia descrizione migliorata. – Rag

15

EDIT

La maggior parte delle risposte si concentrano sul basso API di livello con attese e notifica o condizioni (che funzionano più o meno allo stesso modo): è difficile da ottenere quando non si è abituati ad esso. Dimostrazione: 2 di queste risposte non usano aspettare correttamente.
java.util.concurrent offre un'API di alto livello in cui sono state nascoste tutte quelle complessità.

IMHO, non è possibile utilizzare un modello di attesa/notifica quando esiste una classe integrata nel pacchetto concorrente che raggiunge lo stesso.


Un CountDownLatch con un conteggio iniziale di 1 fa esattamente questo:

  • Quando la condizione diventa vero, chiamare latch.countdown();
  • nel vostro thread in attesa, utilizzare: boolean ok = latch.await(1, TimeUnit.SECONDS);

Esempio di contrived:

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

Nota: CountDownLatches sono thread-safe.

+0

Questa è davvero la migliore risposta, una soluzione pulita ed efficiente ... –

-1

Sembra che ciò che si vuole fare è controllare la condizione e poi se è falso, attendere timeout. Quindi, nell'altra discussione, notifyAll una volta completata l'operazione.

Cameriere

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

Questo dovrebbe fare il trucco per voi. Puoi anche farlo usando un blocco di selezione, ma dovresti controllare il timeout ogni volta che esegui il ciclo. Il codice potrebbe essere in realtà un po 'più semplice.

+3

non dovresti usare aspettare fuori da un ciclo while. – assylias

1

Stavo cercando una soluzione come quello che offre Awaitility. Penso di aver scelto un esempio errato nella mia domanda. Ciò che intendevo era in una situazione in cui ti aspetti un evento asincrono che viene creato da un servizio di terze parti e il cliente non può modificare il servizio per offrire notifiche. Un esempio più ragionevole sarebbe quello sotto.

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

Precisamente, 'Condition' o altre soluzioni basate su asincronicità Java finiscono per ingombrare il codice e leggere in modo davvero innaturale i casi con librerie di terze parti che non forniscono alcun punto di ingresso a un'attesa sincrona sull'elaborazione in background. Ho finito per usare anche 'awaitility' anche nel codice non test, in quanto è la soluzione migliore in questo caso, specialmente dal punto di vista della leggibilità/manutenibilità. – desseim

2

Awaitility offre una soluzione semplice e pulita:.

attendere() atmost (10 secondi), .fino (() -> condizione());

Problemi correlati