JDK-6458662 : ThreadPoolExecutor poolSize might shrink below corePoolSize after timeout
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2006-08-09
  • Updated: 2011-05-17
  • Resolved: 2011-05-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b08Fixed
Related Reports
Relates :  
Description
The spec for ThreadPoolExecutor requires poolSize to not drop below
corePoolSize due to timeouts.

But:

import java.util.*;
import java.util.concurrent.*;

public class TimeOutShrink {
    static void print(ThreadPoolExecutor pool) {
	System.out.printf("poolSize=%d core=%d max=%d%n",
			  pool.getPoolSize(),
			  pool.getCorePoolSize(),
			  pool.getMaximumPoolSize());
    }

    public static void main(String[] args) throws Throwable {
	final int n = 4;
	final CyclicBarrier barrier = new CyclicBarrier(2*n+1);
	final ThreadPoolExecutor pool
	    = new ThreadPoolExecutor(n, 2*n, 5L, TimeUnit.SECONDS,
				     new SynchronousQueue<Runnable>());
	final Runnable r = new Runnable() { public void run() {
	    try {
		barrier.await();
		barrier.await();
	    } catch (Throwable t) { t.printStackTrace(); }}};

	for (int i = 0; i < 2*n; i++)
	    pool.execute(r);
	barrier.await();
	print(pool);
	barrier.await();
	Thread.sleep(10*1000);
	print(pool);
	pool.shutdown();
	pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
    }
}

prints:

poolSize=8 core=4 max=8
poolSize=0 core=4 max=8

but should print

poolSize=8 core=4 max=8
poolSize=4 core=4 max=8

Comments
EVALUATION In the following fragment from getTask, the test poolSize > corePoolSize is performed to decide whether to do a timed poll. But the same test needs to be performed afterwards as well to decide whether this worker can exit. int state = runState; if (state > SHUTDOWN) return null; Runnable r; if (state == SHUTDOWN) // Help drain queue r = workQueue.poll(); else if (poolSize > corePoolSize || allowCoreThreadTimeOut) r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); else r = workQueue.take(); if (r != null) return r; if (workerCanExit()) { if (runState >= SHUTDOWN) // Wake up others interruptIdleWorkers(); return null; } But workerCanExit decides it's always alright to exit if workQueue.isEmpty() which means the poolSize might go down to zero.
09-08-2006