28

Is there a library in Java that does the following? A thread should repeatedly sleep for x milliseconds until a condition becomes true or the max time is reached.

This situation mostly occurs when a test waits for some condition to become true. The condition is influenced by another thread.

[EDIT]Just to make it clearer, I want the test to wait for only X ms before it fails. It cannot wait forever for a condition to become true. I am adding a contrived example.

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 
    }
}
Rag
  • 1,363
  • 4
  • 19
  • 36
  • If there's another thread controlling it, can you set up an actual 'done' signal with wait/notify instead of polling? – Thomas Jan 22 '13 at 17:32
  • _The condition is influenced by another thread_ Can I say that I can use _Thread join_ here? Anyways +1 for the question. – Amarnath Jan 22 '13 at 17:36

8 Answers8

28

EDIT

Most answers focus on the low level API with waits and notifies or Conditions (which work more or less the same way): it is difficult to get right when you are not used to it. Proof: 2 of these answers don't use wait correctly.
java.util.concurrent offers you a high level API where all those intricacies have been hidden.

IMHO, there is no point using a wait/notify pattern when there is a built-in class in the concurrent package that achieves the same.


A CountDownLatch with an initial count of 1 does exactly that:

  • When the condition becomes true, call latch.countdown();
  • in your waiting thread, use : boolean ok = latch.await(1, TimeUnit.SECONDS);

Contrived example:

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

Note: CountDownLatches are thread safe.

assylias
  • 321,522
  • 82
  • 660
  • 783
19

Awaitility offers a simple and clean solution:

await().atMost(10, SECONDS).until(() -> condition());
Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78
thisismydesign
  • 21,553
  • 9
  • 123
  • 126
  • great library for the purpose. specially useful in integration tests where server is asynchronous and you don't want your test to finish before the other end under test has not done the work. – WinterBoot May 25 '22 at 17:44
8

You should be using a Condition.

If you would like to have a timeout in addition to the condition, see await(long time, TimeUnit unit)

Alex DiCarlo
  • 4,851
  • 18
  • 34
1

I was looking for a solution like what Awaitility provides. I think I chose an incorrect example in my question. What I meant was in a situation where you are expecting an asynchronous event to happen which is created by a third party service and the client cannot modify the service to offer notifications. A more reasonable example would be the one below.

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;
        }
    }
}
Rag
  • 1,363
  • 4
  • 19
  • 36
  • Precisely, `Condition` or other Java asynchronicity based solutions end up cluttering the code and read really unnatural for cases with 3rd party libraries which don't give any entry point to a synchronous wait on their background processing. I ended up using `awaitility` too even in non-testing code, as it is the best adapted solution in this case, especially from a readability / maintainability standpoint. – desseim Mar 14 '14 at 16:20
0

You should not sleep and check and sleep and check. You want to wait on a condition variable and have the condition change and wake up the thread when its time to do something.

djechlin
  • 59,258
  • 35
  • 162
  • 290
jco.owens
  • 535
  • 2
  • 5
0

I solved this issue by applying this universal method utilizing functional interface:

private void waitUntilConditionIsMet(BooleanSupplier awaitedCondition, int timeoutInSec) {
    boolean done;
    long startTime = System.currentTimeMillis();
    do {
        done = awaitedCondition.getAsBoolean();
    } while (!done && System.currentTimeMillis() - startTime < timeoutInSec * 1000);
}

Then you can implement:

class StateHolderTest{
    @Test
    public void shouldTurnActive(){
        StateHolder holder = new StateHolder();
        waitUntilConditionIsMet(() -> holder.isActive(), 10);
        assertTrue(holder.isActive);
    }
}
Igor Lopatka
  • 121
  • 2
  • 3
0

You could use something like this but replace the "true" with the condition you want to check and it will sleep until it becomes true.

sleepUntil(() -> true);

//Sleeps this thread until the condition is true.
public static void sleepUntil(BooleanSupplier condition) {
    while(!condition.getAsBoolean()) {
        sleep(10);
    }
}

//Sleeps this thread with the given amount of milliseconds
public static void sleep(int ms) {
    try {
         Thread.sleep(ms);
    } catch (Exception e) {}
}
-1

It seems like what you want to do is check the condition and then if it is false wait until timeout. Then, in the other thread, notifyAll once the operation is complete.

Waiter

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

That should do the trick for you. You can also do it using a spin lock, but you would need to check the timeout every time you go through the loop. The code might actually be a bit simpler though.

Patrick Cornelissen
  • 7,968
  • 6
  • 48
  • 70
Jonathan
  • 3,369
  • 4
  • 22
  • 27