Thread.sleep
The Thread.sleep() method effectively "pauses" the current thread for a given period of time.
We used it in our very first threading example to make threads display
a message periodically, sleeping between messages. From the outset, it's important to
be aware of the following:
- it is always the current thread that is put to sleep;
- the thread might not sleep for the required time (or even at all);
- the sleep duration will be subject to some system-specific granularity,
typically 1ms;
- while sleeping, the thread still owns synchronization locks it has
acquired;
- the sleep can be interrupted (sometimes useful
for implementing a cancellation function);
- calling sleep() with certain values can have some subtle, global effects on the OS (see below),
and vice versa, other threads and processes running on the system can have subtle effects on the
observed sleep duration.
With these caveats, the general idea is that we call Thread.sleep(), passing in
a number of milliseconds (or milliseconds plus nanoseconds) that we want to sleep for:
// sleep for about 2 seconds
Thread.sleep(2000L);
// sleep for about 2.5 seconds
Thread.sleep(2000L, 500000L);
Sleep behaviour and granularity
On both Windows and Linux, the VM can generally offer a maximum sleep granularity
of 1ms, and will achieve this quite accurately on a "quiet" system. Figure 1 shows
the mean observed sleep duration against requested duration on a uniprocessor machine
running Windows XP (each point is the mean of around 50 measured sleeps of the
requested duration).
The sleep behaviour illustrated by this graph and reasons for it will be discussed in more detail in the
sections below.
Nanosecond sleeps?
The aim of the nanosecond version is (presumably) to offer potential support for
"real time" operating systems that can offer this level of fine-tuned sleep.
In practice, no mainstream operating system that I'm aware
of can sleep with a granularity of lower than 1ms, and Sun's J2SE implementation
actually just rounds to the nearest millisecond!
Figure 1: Mean sleep duration against requested duration under different load circumstances in Windows XP.
How does sleep work?
To understand some of the behaviour we see in Figure 1, it's worth understanding a little
about how thread sleeping actually works.
The Thread.sleep() method essentially interacts with the
thread scheduler to put the current
thread into a wait state for the required interval. In order to allow interruption,
the implementation may not actually use the explicit sleep function that most
OS's provide. (For example, Sun's VM under Windows implements Thread.sleep() by waiting for
a thread interrupt event with a timeout, via WaitForMultipleObjects).
The granularity of sleeps is generally bound by the
thread scheduler's interrupt period. In Linux, this interrupt period
is generally 1ms in recent kernels (2.6.8 onwards).
In Windows, the scheduler's interrupt period is normally around 10 or 15 milliseconds
(which I believe is dictated by the processor), but a higher period can be requested in software
and the Hotspot JVM does so when it deems necessary (but see the section on bugs below).
Assuming an interrupt every 1ms, then when the required whole number of milliseconds has elapsed,
whether the thread is "woken up" (i.e. scheduled on to a CPU) on that particular interrupt depends
essentially on whether there is a higher-priority thread that is ready to run at that moment.
On Windows, the thread that is ready to be woken will be given a small temporary priority boost so
that it is more likely to "cut in" at the right moment. But if that still isn't enough, then the
wakeup will be delayed. In Figure 1, the shape of the red line shows the pattern in mean sleep when
a DVD player application is running in the foreground at the same time as the sleep test. Since the
DVD player (presumably) has some above-normal priority threads to manage video and audio, there is
often a chance that the woken thread will still have to wait its turn. The yellow and green
lines show what happens when we lower and raise the priority of the sleeping Java thread (to
MIN_PRIORITY and MAX_PRIORITY respectively): raising the priority alleviates
the problem to some extent, but does not eliminate delay. Some of the spikes on the graph, particularly
in the case of the normal-priority thread (in red) are probably due to slightly complex interactions between
requested sleep time and quantum. With certain combinations of
thread priority and requested sleep duration, we seem more likely to hit "worse cases" where our thread
must wait for a higher-priority thread to complete its quantum. And every 6 sleeps, our thread will also
run out of quantum and have to wait for threads of equal priority. On the test machine, the interrupt period
was 15ms, and so the default thread quantum 30ms: note a high spike around 30ms, and lower spikes
around 15 and 45ms.
Issues with Thread.sleep()
On the next page, we examine various issues and bugs with Thread.sleep() not
fully discussed above.
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.