Domanda Dormi e controlla fino a quando la condizione è vera


Esiste una libreria in Java che esegue quanto segue? UN thread dovrebbe ripetutamente sleep per x millisecondi fino a quando una condizione diventa vera o viene raggiunto il tempo massimo.

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

[EDIT] Solo per renderlo 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 
    }
}

18
2018-01-22 17:30


origine


risposte:


MODIFICARE

La maggior parte delle risposte si concentra sull'API di basso livello con attese e notifiche o condizioni (che funzionano più o meno allo stesso modo): è difficile avere ragione quando non ci si è abituati. Dimostrazione: 2 di queste risposte non usano aspettare correttamente.
java.util.concurrent ti offre un'API di alto livello in cui tutte queste complessità sono state nascoste.

IMHO, non c'è motivo di usare un pattern wait / notify quando c'è una classe built-in nel pacchetto concorrente che raggiunge lo stesso.


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

  • Quando la condizione diventa vera, chiama latch.countdown();
  • nel tuo thread di attesa, usa: boolean ok = latch.await(1, TimeUnit.SECONDS);

Esempio di esempio

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 è thread-safe.


17
2018-01-22 17:32



Dovresti usare a Condition.

Se si desidera avere un timeout in aggiunta alla condizione, vedere await(long time, TimeUnit unit)


7
2018-01-22 17:54



Awaitility offre una soluzione semplice e pulita:

await (). atMost (10, SECONDS) .until (() -> condition ());


2
2017-10-11 08:00



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

1
2017-11-28 21:17



Non dovresti 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
2018-01-22 17:32



Sembra che quello che vuoi fare sia controllare la condizione e poi se è falso, aspetta fino al 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 te. 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.


-1
2018-01-22 18:07