When using ScheduledExecutorService with a core pool size of 0, a JDK bug can cause CPU usage to spike to 100%. This occurs due to an infinite loop in the getTask method of ThreadPoolExecutor when keepAliveTime is set to 0 nanoseconds.
Problem Demonstration
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(0);
executor.schedule(() -> {
System.out.println("Business logic");
}, 60, TimeUnit.SECONDS);
executor.shutdown();
}
This code creates a thread pool with 0 core threads. The scheduled task runs after 60 seconds, but during this period, CPU usage spikes to 100%.
Root Cause
The issue stems from ThreadPoolExecutor.getTask():
- When
corePoolSize = 0,timedbecomestrue(sincewc > corePoolSize). - The method calls
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)withkeepAliveTime = 0. - This creates a tight loop where the thread continuously checks for new tasks.
JDK Fix
The bug was fixed in JDK 9 by changing the default keepAliveTime from 0 nanoseconds to 10 milliseconds (DEFAULT_KEEPALIVE_MILLIS). This prevents the infinite loop scenario.
Another Related Bug (JDK-8051859)
When using scheduleWithFixedDelay with extremely large delays (e.g., Long.MAX_VALUE microseconds), the calculasion overflows, causing the next execution time to be set to 292 years in the past. The fix modifies the time calculation to avoid overflow.
Key Takeaways
- Avoid setting
corePoolSize = 0inScheduledExecutorService. - Be cautious with extremely large delay values in scheduled tasks.
- The bugs were addressed in JDK 9, so upgrading may resolve these issues.