To main content

The best alternative to Thread.sleep() in Java

Published by Benjamin Marwell on

In case you use Thread.sleep() in either your Java code or your unit tests, an article by Marcio Endo has you covered. However, I think two alternatives are really worth mentioning.

Alternatives introduction: avoid Thread.sleep in your Java code

Well, in Marcio‘s examples, you had to modify your original Java code to make things work. I do not think you should be needing to modify your Java code just for the sake of tests — only adding getters and setters (preferrably protected) should be needed. That said, let‘s look again at the original class:

No alternative: Using TimeUnit instead of Thread.sleep()

Instead of using Thread.sleep(1000) in your tests, you could just use a simpler API: TimeUnit#sleep(long timeout):

try {
  TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException intEx) {
  // handle... log or rethrow
}

As you can see, you will still have to deal with all the Exception handling and you will have to create a while loop around it. That said, this is not a viable alternative to Thread.sleep() except it might be a little easier to read.

Alternative 1: Use awaitility for tests

If you only needed the timeout in your JUnit test, you might not want to heavily alter your class for no reason than just testing. Given this premise, there is a nice utility which can help you with this: awaitility.

Here is a complete test class:

This will return 22(ms).

The neat thing about this solution is that you do not need to do anything about threading. It also supports both Duration and long plus TimeUnit in its APIs. Usually, you do not need to set pollInterval(). I only did it because the default seems to be 100ms to get a somewhat more realistic value.

This is a very tiny dependency with an easy-to-understand API. I use it a lot in Maven modules where I do threading and async operations.

If you start the thread using a CompletableFuture, you can get entirely rid of the thread management in the counter class. This is recommended, as it is not the Counter class‘s responsibility to deal with threads.

Another advantage to TestNG is, that you do not set a global test method timeout, but instead a timeout for each logical unit.

Alternative 2: Using CompletableFutures

If you need to time out some background operation in your production code, you can use the java.concurrent API. This API is also available in JakartaEE, whith only one tiny caveat —  luckily, it is easy to remember.

Using in any JavaSE application

This will return 33(ms) on my machine.

As we are using streams, it is easy to extend from here by using a second CompletableFuture (maybe a completed one) as a fallback. That way we would not need to specify a default after the findFirst() method.

Using in a JakartaEE web application

Web applications are not allowed to start threads on their own. Instead, just inject a ManagedExecutorService as a field and add it as a second parameter to the CompletableFuture#supplyAsync(). That‘s all there is to it!

Conclusion

The best alternative to using Thread.sleep() in Java is to avoid it by using either the awaitility library for your tests or using the java.concurrent API in your programme code.