2013-01-22 7 views
17

Gibt es eine Java-Bibliothek, die Folgendes ausführt? A thread sollte wiederholt sleep für x Millisekunden, bis eine Bedingung erfüllt oder die maximale Zeit erreicht ist.Schlafen und überprüfen, bis die Bedingung erfüllt ist

Diese Situation tritt meistens auf, wenn ein Test darauf wartet, dass eine Bedingung erfüllt wird. Die Bedingung wird von einem anderen thread beeinflusst.

[EDIT] Nur um es klarer zu machen, möchte ich den Test auf nur X ms warten, bevor es fehlschlägt. Es kann nicht ewig auf eine Bedingung warten, um wahr zu werden. Ich füge ein künstliches Beispiel hinzu.

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

Wenn es einen anderen Thread gibt, der es kontrolliert, können Sie ein tatsächliches 'Fertig'-Signal mit wait/notify einrichten, statt das Polling zu benutzen? – Thomas

+0

_Die Bedingung wird von einem anderen Thread beeinflusst_ Kann ich sagen, dass ich _Thread join_ hier verwenden kann? Anyways +1 für die Frage. – Amarnath

Antwort

0

Sie sollten nicht schlafen und überprüfen und schlafen und überprüfen. Sie möchten auf eine Bedingungsvariable warten und die Bedingung ändern und den Thread aufwecken, wenn es Zeit ist, etwas zu tun.

+0

siehe meine verbesserte Beschreibung. – Rag

15

EDIT

meisten Antworten konzentrieren sich auf dem niedrigen Niveau API mit wartet und benachrichtigt oder Bedingungen (die die gleiche Art und Weise mehr oder weniger Arbeit): es schwierig ist, richtig zu machen, wenn Sie es nicht gewohnt sind. Beweis: 2 dieser Antworten verwenden nicht korrekt.
java.util.concurrent bietet Ihnen eine High-Level-API, wo all diese Feinheiten versteckt wurden.

IMHO, es gibt keinen Punkt mit einem Wait/Notify-Muster, wenn es eine integrierte Klasse in der gleichzeitigen Paket, die das gleiche erreicht.


A CountDownLatch mit einer Anfangszahl von 1 tut genau das:

  • Wenn die Bedingung wahr wird, latch.countdown();
  • in Ihrem wartenden Thread nennen, zu verwenden: boolean ok = latch.await(1, TimeUnit.SECONDS);

Contrived Beispiel:

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

Hinweis: CountDownLatches sind threadsicher.

+0

Dies ist wirklich die beste Antwort, eine saubere und effiziente Lösung ... –

-1

Es scheint, wie das, was Sie, die Bedingung tun möchten, ist zu überprüfen und dann, wenn es falsch ist warten, bis Timeout. Dann notifyAll im anderen Thread, sobald der Vorgang abgeschlossen ist.

Kellner

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

, die den Trick für Sie tun sollten. Sie können dies auch mit einer Drehsperre tun, aber Sie müssen das Zeitlimit jedes Mal überprüfen, wenn Sie die Schleife durchlaufen. Der Code könnte jedoch tatsächlich ein bisschen einfacher sein.

+3

sollten Sie nicht außerhalb einer while-Schleife warten. – assylias

1

Ich war auf der Suche nach einer Lösung wie das, was Avaitility bietet. Ich glaube, ich habe ein falsches Beispiel in meiner Frage gewählt. Was ich meinte, war in einer Situation, in der Sie erwarten, dass ein asynchrones Ereignis auftritt, das von einem Drittanbieterdienst erstellt wird, und der Client den Dienst nicht ändern kann, um Benachrichtigungen anzubieten. Ein vernünftigeres Beispiel wäre das folgende.

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

Genauer gesagt, "Condition" oder andere Java-asynchro- nizitätsbasierte Lösungen überfluten den Code und lesen wirklich unnatürlich für Fälle mit Bibliotheken von Drittanbietern, die keinen Einstiegspunkt für ein synchrones Warten auf ihre Hintergrundverarbeitung geben. Am Ende habe ich '' awarenessity' 'auch in nicht-testendem Code verwendet, da es in diesem Fall die am besten angepasste Lösung ist, insbesondere aus Sicht der Lesbarkeit/Wartbarkeit. – desseim

2

Awaitility bietet eine einfache und saubere Lösung.

erwarten() atmost (10, SEKUNDEN) .bis (() -> Zustand());

Verwandte Themen