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

后端 未结 3 985
栀梦
栀梦 2020-12-06 02:53

I was going through Java Concurrency In Practice and got stuck at the 8.3.1 Thread creation and teardown topic. The following footnote warn

相关标签:
3条回答
  • 2020-12-06 03:06

    While running this program in jdk 1.5,1.6,1.7 and 1.8, I found different implementations of ThreadPoolExecutor#execute(Runnable) in 1.5,1.6 and 1.7+. Here's what I found:

    JDK 1.5 implementation

     //Here poolSize is the number of core threads running.
    
     public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        for (;;) {
            if (runState != RUNNING) {
                reject(command);
                return;
            }
            if (poolSize < corePoolSize && addIfUnderCorePoolSize(command))
                return;
            if (workQueue.offer(command))
                return;
            Runnable r = addIfUnderMaximumPoolSize(command);
            if (r == command)
                return;
            if (r == null) {
                reject(command);
                return;
            }
            // else retry
        }
    }
    

    This implementation does not create a thread when corePoolSize is 0, therefore the supplied task does not execute.

    JDK 1.6 implementation

    //Here poolSize is the number of core threads running.
    
      public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
            if (runState == RUNNING && workQueue.offer(command)) {
                if (runState != RUNNING || poolSize == 0)
                    ensureQueuedTaskHandled(command);
            }
            else if (!addIfUnderMaximumPoolSize(command))
                reject(command); // is shutdown or saturated
        }
    }
    

    JDK 1.6 creates a new thread even if the corePoolSize is 0.

    JDK 1.7+ implementation(Similar to JDK 1.6 but with better locks and state checks)

        public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }
    

    JDK 1.7 too creates a new thread even if the corePoolSize is 0.

    So, it seems that corePoolSize=0 is a special case in each versions of JDK 1.5 and JDK 1.6+.

    But it is strange that the book's explanation doesn't match any of the program results.

    0 讨论(0)
  • 2020-12-06 03:07

    Seems like it was a bug with older java versions but it doesn't exist now in Java 1.8.

    According to the Java 1.8 documentation from ThreadPoolExecutor.execute():

         /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         * ....
         */
    

    In the second point, there is a recheck after adding a worker to the queue that if instead of queuing the task, a new thread can be started, than rollback the enqueuing and start a new thread.

    This is what is happening. During first check the task is queued but during recheck, a new thread is started which executes your task.

    0 讨论(0)
  • 2020-12-06 03:14

    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)
    0 讨论(0)
提交回复
热议问题