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:

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 per requested duration

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.