Summary
Improve Thread.sleep(long millis, int nanos)
to perform sub-millisecond sleeps.
Problem
Java API has the Thread.sleep(long millis, int nanos)
method exposed to users. The documentation for that method clearly says the precision and accuracy are dependent on the underlying system behavior.
However, the method always rounds up to 1ms before calling into VM, therefore the lowest sleep duration the user can ask for is 1ms, even if they asked for 1ns sleep. This means users cannot do the sub-millisecond precision sleeps, even when the underlying platform allows it. Many POSIX platforms allow sleeping for tens of microseconds. Since Thread.sleep(millis, nanos)
does not help in this case, power users have discovered and now widely employ dubious tricks to access the sub-millisecond sleeps, like LockSupport.parkNanos
, which was originally implemented solely for java.util.concurrent
primitives support.
Solution
The solution is to improve current Thread.sleep(millis, nanos)
for sub-millisecond sleeps. Fortunately, the API is ready for this, and we only need an implementation change to drop the 1ms rounding and translate the existing Java call to existing POSIX interfaces. The VM implementation already handles the actual elapsed time tracking well, looping with platform sleep calls until the sleep time expires. This provides us with additional safety in case platform primitives misbehave: a) if platform sleep returns too early, we would do another loop iteration; b) if platform sleep return too late, we would return normally, and the time delay should be no worse than calling the same primitive with 1ms rounding.
The current improvement targets POSIX systems, which excludes only Windows at this point. Future research/implementation work in Windows code could provide the similar improvements to sleep granularity.
Specification
There are no specification changes. The new behavior is covered by the existing specification.
There are two behavioral risks:
This improvement may lead to surprises for users who ask for less than 1ms sleeps with Thread.sleep(0, nanos)
and either deliberately or accidentally rely on 1ms rounding to get at least 1ms sleep. This risk should be minimal, since most user code calls Thread.sleep(millis)
, ignoring the Thread.sleep(0, nanos)
path completely. For users who do Thread.sleep(0, nanos)
, new behavior should be the desired one.
The implementation simplifies some paths to pass the sleep delays in jlong nanos
, which means both Thread.sleep(millis)
and Thread.sleep(millis, nanos)
would return after ~292 years even with the largest time requested. This seems to be a practical tradeoff for implementation simplicity, and it is already accepted on "sleep" path for Virtual Threads. This behavior still falls in line with the spec that has the caveat about the precision and accuracy of system timers, and this "early" return would be not observable in practice, without the use of century-wide system time adjustments or access to a time machine.
- csr of
-
JDK-8305092 Improve Thread.sleep(millis, nanos) for sub-millisecond granularity
- Resolved
- relates to
-
JDK-8306660 Release Note: `Thread.sleep(millis, nanos)` Is Now Able to Perform Sub-Millisecond Sleeps
- Resolved
-
JDK-8210004 Thread.sleep(millis, nanos) timeout returns early
- Resolved