This discussion is archived
0 Replies Latest reply: Nov 20, 2012 12:33 PM by 975102 RSS

java 7 ThreadPoolExecutor javadoc inaccuracy & odd behavior

975102 Newbie
Currently Being Moderated
Hi -- I'm hoping somebody can point me to the correct dev list to notify of an inaccuracy in the java 7 and 8 javadoc. ThreadPoolExecutor lines 170 and 171 state, "And the value of the maximumPoolSize therefore doesn't have any effect." However, getTask (called by a new thread when firstTask is null) clearly references the maximumPoolSize on line 1032 (I'm looking at 1.7.0_10 and a version of 1.8.0). It looks like the statement was true in java 6.

In addition the the javadoc being slightly inaccurate, the ThreadPoolExecutor behavior seems to have changed in a way I've not seen documented. If it has been, I'd appreciate a link to the documentation so I can read over it. But the situation is this:

Suppose a ThreadPoolExecutor which uses an unbounded blocking queue has a corePoolSize and maximumPoolSize of 4. Suppose I submit 5 tasks so that 1 task is queued, then set the corePoolSize to 5. In java6, this would cause an additional thread to be allocated and the task would get executed. In java7, this doesn't happen (apparently bounded by the check against maximumPoolSize mentioned above). It's actually a little worse than that as the Thread gets allocated & added to the pool, but it does not handle the waiting task. Though the case where corePoolSize > maximumPoolSize seems exceptional, it is possible. In my case, I was trying to allow another Thread in the ThreadPoolExecutor so that my current thread could block & wait on a newly submitted thread to return. It deadlocked instead.

I worked up a bit of code to demonstrate the change in behavior. It runs to completion in both cases, but the java7 version takes longer because it has to re-use a Thread. If you put a breakpoint on ThreadPoolExecutor:1128 (the call to processWorkerExit), you should see Threads die & exit the pool while there is still work in the pool.

<pre>
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MaxPoolSizeDemo
{
static ThreadPoolExecutor tpe;
static long startMillis = System.currentTimeMillis();

public static void main(String[] args) {
LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>(10);
int corePoolSize = 4;
int maximumPoolSize = 4;

tpe = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 5, TimeUnit.SECONDS, taskQueue);

for (int i = 0; i < corePoolSize; i++) {
submit(i);
}
submit(corePoolSize + 1);
System.out.println("waiting: " + taskQueue.size());
tpe.setCorePoolSize(tpe.getCorePoolSize() + 1);

System.out.println("pool size: " + tpe.getPoolSize());
tpe.setCorePoolSize(tpe.getCorePoolSize() + 1);
System.out.println("pool size: " + tpe.getPoolSize());
System.out.println("max pool size: " + tpe.getMaximumPoolSize());
tpe.setCorePoolSize(tpe.getCorePoolSize() + 1);

System.out.println("core pool size: " + tpe.getCorePoolSize());
tpe.shutdown();
}

public static long getElapsedMillis() {
return System.currentTimeMillis() - startMillis;
}

static void submit(final int taskId) {
tpe.submit(new PauseTask(taskId));
}

private static class PauseTask implements Runnable
{
int taskId;

public PauseTask(int taskId) {
this.taskId = taskId;
}

@Override
public void run() {
System.out.println(getElapsedMillis() + " - " + Thread.currentThread().getName()
+ " running:" + taskId);
try {
Thread.sleep(10000);
System.out.println(getElapsedMillis() + " - " + Thread.currentThread().getName()
+ " waking/dying:" + taskId);
}
catch (InterruptedException e) {
Thread.interrupted();
System.out.println(getElapsedMillis() + " - " + Thread.currentThread().getName()
+ " was interrupted:" + taskId);
}
}
}
}
</pre>

Java 6 behavior:
0 - pool-1-thread-1 running:0
0 - pool-1-thread-3 running:2
0 - pool-1-thread-2 running:1
0 - pool-1-thread-4 running:3
waiting: 1
pool size: 5
0 - pool-1-thread-5 running:5
pool size: 6
max pool size: 4
core pool size: 7
10001 - pool-1-thread-2 waking/dying:1
10001 - pool-1-thread-4 waking/dying:3
10001 - pool-1-thread-3 waking/dying:2
10001 - pool-1-thread-1 waking/dying:0
10001 - pool-1-thread-5 waking/dying:5

Java 7 behavior:
0 - pool-1-thread-1 running:0
0 - pool-1-thread-4 running:3
waiting: 1
0 - pool-1-thread-3 running:2
0 - pool-1-thread-2 running:1
pool size: 5
pool size: 5
max pool size: 4
core pool size: 7
10000 - pool-1-thread-1 waking/dying:0
10000 - pool-1-thread-2 waking/dying:1
10000 - pool-1-thread-4 waking/dying:3
10000 - pool-1-thread-3 waking/dying:2
10000 - pool-1-thread-1 running:5
20001 - pool-1-thread-1 waking/dying:5

Edited by: tab126 on Nov 20, 2012 12:32 PM
grammar

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points