JDK-6450205 : ThreadPoolExecutor does not replace throwing threads
  • 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-07-19
  • Updated: 2011-05-18
  • Resolved: 2011-05-18
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
Neal wrote:

>> Executor seems to take roughly a minute to replace threads that have
>> died, rather than replacing them immediately.

--------------
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class Bug5 {
    private static final AtomicInteger count
	= new AtomicInteger(0);

    private static final Thread.UncaughtExceptionHandler handler
	= new Thread.UncaughtExceptionHandler() {
		public void uncaughtException(Thread t, Throwable e) {
		    count.getAndIncrement(); }};

    static void report(String label, ThreadPoolExecutor tpe) {
	System.out.printf("%10s: active=%d, submitted=%d, completed=%d%n",
			  label,
			  Thread.activeCount() - 1,
			  tpe.getTaskCount(),
			  tpe.getCompletedTaskCount());
    }

    public static void main(String[] args) throws Throwable {
	Thread.setDefaultUncaughtExceptionHandler(handler);

	final int count = 8;
	final CyclicBarrier barrier = new CyclicBarrier(count + 1);
	ThreadPoolExecutor tpe =
	    new ThreadPoolExecutor(10, 30, 1L, TimeUnit.HOURS,
				   new LinkedBlockingQueue<Runnable>());

	report("newborn", tpe);

	for (int i = 0; i < count; i++)
	    tpe.execute(new Runnable() {
		    public void run() {
			try { barrier.await(); barrier.await(); }
			catch (Throwable t) { t.printStackTrace(); }
		    }});

	barrier.await();
	report("started", tpe);

	barrier.await();
	Thread.sleep(1000);
	report("idling", tpe);

	for (int i = 0; i < count; i++)
	    tpe.execute(new Runnable() {
		    public void run() {
			throw new RuntimeException();
		    }});

	Thread.sleep(1000);

	report("thrown", tpe);

	System.out.printf("Shutting down...%n");
	tpe.shutdown();
	tpe.awaitTermination(1L, TimeUnit.HOURS);

	report("terminated", tpe);
    }
}
--------------

 $ jver 6 jr Bug5
==> javac -source 1.6 -Xlint:all Bug5.java
==> java -esa -ea Bug5
   newborn: active=0, submitted=0, completed=0
   started: active=8, submitted=8, completed=0
    idling: active=8, submitted=8, completed=8
    thrown: active=2, submitted=8, completed=8
Shutting down...
terminated: active=0, submitted=8, completed=8

The active count should not go down to 2.

Comments
EVALUATION Contribution forum : https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?forumID=1463&messageID=17771
22-12-2006

EVALUATION Submitter is correct. When a thread dies, there is code to keep the threads in the pool from going to zero, but there is no provision for adding a thread to keep the thread count at least corePoolSize. The documentation is not clear about what happens to tasks that throw. Applications do well to not allow this to happen, perhaps simply by using submit() instead of execute(). If an application has tasks that throw, and a backlog of tasks in the queue, the pool will tend to shrink to one thread, which will manifest as a hard-to-diagnose performance problem. Especially hard-to-diagnose if occasional calls to execute cause the pool to speed up again!
19-07-2006