Bugs and issues with Thread.sleep()

When using Thread.sleep in Java, there are a few bugs and caveats to be aware of. On this page, we discuss how to guarantee that a thread sleeps for a certain amoung of time, potential bugs to watch out for and the issue of "oversleeping".

"Guaranteeing" a certain sleep time

For reasons detailed in our discussion on how Thread.sleep() works, there is no guarantee as to how long the thread will actually sleep: this can depend on factors such as what other threads are competing for the CPU, and on the sleep granularity that a given OS can fulfil.

The next-best solution is usually to time the sleep, and then make some adjustment depending on how long the thread actually slept for. For example, to sleep for at least a given amount of time, we can call sleep in a loop until the total amount of time slept for reaches at least the required number of milliseconds:

public void sleepAtLeast(long millis) throws InterruptedException {
  long t0 = System.currentTimeMillis();
  long millisLeft = millis;
  while (millisLeft > 0) {
    Thread.sleep(millisLeft);
    long t1 = System.currentTimeMillis();
    millisLeft = millis - (t1 - t0);
  }
}

In an animation thread or similar case where we want spaces between sleeps to be as even as possible, a common solution is to adjust the next sleep to compensate for undersleeping/oversleeping (or, put another way, calculate how many milliseconds there are between now and the next expected time for the task to be performed, and sleep for that many milliseconds). The java.util.Timer class effectively provides an implementation of this approach.

Historic bugs with Thread.sleep() in Windows XP

Historically, there have been a few bugs related to Thread.sleep() under Windows, notably in Windows XP. One issue stemmed from the fact that, to try and alleviate the low granularity, the JVM would make a special call to set the interrupt period to 1ms while any Java thread was sleeping. This would occur specifically if any Thread.sleep() request was made for an interval that was not a multiple of 10ms. It meant that you were actually make a system-wide change to interrupt behaviour (although generally not such a problem). Generally, the solution worked well enough, but:

For timing-critical applications, an inelegant but practical workaround was to leave a daemon thread running throughout the duration of the application that simply slept for a large prime number of milliseconds (Long.MAX_VALUE will do). This way, the interrupt period would be set once per invocation of the application, minimising the effect on the system clock, and setting the sleep granularity to 1ms even where the default interrupt period was not 15ms.

Scheduling with oversleepy threads

A pragmatic observation illustrated in Figure 1 is that if anything, threads will tend to oversleep rather than undersleep. This means that if you're using Thread.sleep() to schedule something like a MIDI interface that can itself accept a small delay on events sent to it, it may better to deliberately request a lower-than-needed sleep duration, and then pass on the amount of undersleep (within some maximum) to the MIDI device. For example, suppose we need to send a MIDI note in 40 milliseconds' time. So we call Thread.sleep() with a value of 30ms but time that the actual sleep was 35ms. At that point, even though the note isn't due for another 5ms, a busy system may not be able to guarantee returning to our thread within 5ms (or timing within this granularity). So we send the note early, but ask the MIDI interface to delay the note by 5ms. The MIDI interface may or may not be able to honour this delay, but the overall timing error is unlikely to be worse than if we'd asked to sleep the full 40ms and risked sending the note late.


If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.

Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.