Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ThreadPoolExecutor with corePoolSize 0 should not execute tasks until task queue is full

I was going through Java Concurrency In Practice and got stuck at the 8.3.1 Thread creation and teardown topic. The following footnote warns about keeping corePoolSize to zero.

Developers are sometimes tempted to set the core size to zero so that the worker threads will eventually be torn down and therefore won’t prevent the JVM from exiting, but this can cause some strange-seeming behavior in thread pools that don’t use a SynchronousQueue for their work queue (as newCachedThreadPool does). If the pool is already at the core size, ThreadPoolExecutor creates a new thread only if the work queue is full. So tasks submitted to a thread pool with a work queue that has any capacity and a core size of zero will not execute until the queue fills up, which is usually not what is desired.

So to verify this I wrote this program which does not work as stated above.

    final int corePoolSize = 0;
    ThreadPoolExecutor tp = new ThreadPoolExecutor(corePoolSize, 1, 5, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>());

    // If the pool is already at the core size
    if (tp.getPoolSize() == corePoolSize) {
        ExecutorService ex = tp;

        // So tasks submitted to a thread pool with a work queue that has any capacity
        // and a core size of zero will not execute until the queue fills up.
        // So, this should not execute until queue fills up.
        ex.execute(() -> System.out.println("Hello"));
    }

Output: Hello

So, does the behavior of the program suggest that ThreadPoolExecutor creates at least one thread if a task is submitted irrespective of corePoolSize=0. If yes, then what is the warning about in the text book.

EDIT: Tested the code in jdk1.5.0_22 upon @S.K.'s suggestion with following change:

ThreadPoolExecutor tp = new ThreadPoolExecutor(corePoolSize, 1, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(1));//Queue size is set to 1.

But with this change, the program terminates without printing any output.

So am I misinterpreting these statements from the book?

EDIT (@sjlee): It's hard to add code in the comment, so I'll add it as an edit here... Can you try out this modification and run it against both the latest JDK and JDK 1.5?

final int corePoolSize = 0;
ThreadPoolExecutor tp = new ThreadPoolExecutor(corePoolSize, 1, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

// If the pool is already at the core size
if (tp.getPoolSize() == corePoolSize) {
    ExecutorService ex = tp;

    // So tasks submitted to a thread pool with a work queue that has any capacity
    // and a core size of zero will not execute until the queue fills up.
    // So, this should not execute until queue fills up.
    ex.execute(() -> System.out.println("Hello"));
}
tp.shutdown();
if (tp.awaitTermination(1, TimeUnit.SECONDS)) {
    System.out.println("thread pool shut down. exiting.");
} else {
    System.out.println("shutdown timed out. exiting.");
}

@sjlee Have posted the result in comments.

like image 955
Steve Avatar asked Sep 10 '18 07:09

Steve


People also ask

What is queue capacity in ThreadPoolExecutor?

One of the added Advantage of using ThreadPoolTaskExecutor of Spring is that it is well suited for management and monitoring via JMX. The default configuration of core pool size is 1, max pool size and queue capacity as 2147483647.

What is corePoolSize ThreadPoolExecutor?

CorePoolSize: The ThreadPoolExecutor has an attribute corePoolSize that determines how many threads it will start until new threads are only started when the queue is full. MaximumPoolSize: This attribute determines how many threads are started at the maximum. You can set this to Integer.

What happens when thread pool is full?

Since active threads consume system resources, a JVM creating too many threads at the same time can cause the system to run out of memory.

How do you prevent ThreadPoolExecutor?

You can call the cancel() function on the Future object to cancel the task before it has started running. If your task has already started running, then calling cancel() will have no effect and you must wait for the task to complete.


1 Answers

This odd behavior of ThreadPoolExecutor in Java 5 when the core pool size is zero was apparently recognized as a bug and quietly fixed in Java 6.

Indeed, the problem reappeared in Java 7 as a result of some code reworking between 6 and 7. It was then reported as a bug, acknowledged as a bug and fixed.

Either way, you should not be using a version of Java that is affected by this bug. Java 5 was end-of-life in 2015, and the latest available versions of Java 6 and later are not affected. That section of "Java Concurrency In Practice" is no longer apropos.

References:

  • http://cs.oswego.edu/pipermail/concurrency-interest/2006-December/003453.html (read the entire thread)
  • http://gee.cs.oswego.edu/dl/concurrency-interest/index.html (see the version of ThreadPoolExecutor in the JSR166y bundle.)
  • https://bugs.openjdk.java.net/browse/JDK-7091003)
like image 194
Stephen C Avatar answered Sep 23 '22 12:09

Stephen C