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:
- there was also a bug in Windows whereby repeatedly altering the interrupt period (and
hence, repeatedly sleeping in Java for periods that were not multiples of 10ms) could make the
system clock run fast;
- Hotspot also assumed that the default interrupt period was 10ms, but on some hardware it was 15ms.
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.