具有corePoolSize 0的ThreadPoolExecutor在任务队列已满之前不应执行任务 [英] ThreadPoolExecutor with corePoolSize 0 should not execute tasks until task queue is full

查看:180
本文介绍了具有corePoolSize 0的ThreadPoolExecutor在任务队列已满之前不应执行任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在经历 Java Concurrency In Practice ,并坚持 8.3.1线程创建和拆解主题。以下脚注警告将 corePoolSize 保持为零。

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.


开发人员有时会受到诱惑将核心大小设置为零,这样工作线程最终会被拆除
,因此不会阻止JVM退出,但这会导致线程池中出现一些看起来很奇怪的行为。使用SynchronousQueue作为他们的工作队列
(如newCachedThreadPool那样)。 如果池已经是核心大小,则ThreadPoolExecutor仅在工作队列已满时才创建
新线程。因此,提交到具有任何容量和核心大小为零的工作队列
的线程池的任务将不会执行,直到队列填满
,这通常是
而不是所需的。

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"));
    }

输出
你好

那么,程序的行为是否表明 ThreadPoolExecutor 创建至少一个线程无论 corePoolSize = 0 ,都会提交任务。如果是,那么教科书中的警告是什么。

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.

编辑:测试 jdk1.5.0_22 <中的代码/ strong> on @ SK的建议如下:

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.

但是有了这个改变,程序终止而不打印任何输出。

所以我误解了这些陈述来自这本书?

编辑(@sjlee):在评论中添加代码很难,所以我会添加它在这里编辑...你可以尝试这个修改并针对最新的JDK和JDK 1.5运行吗?

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.");
}


推荐答案

在jdk中运行此程序时1.5,1.6,1.7和1.8,我在1.5,1.6和1.7+中发现了 ThreadPoolExecutor#execute(Runnable)的不同实现。这是我发现的:

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实施

 //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
    }
}

corePoolSize 为0时,此实现不会创建线程,因此提供的任务不会执行。

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

JDK 1.6实施

//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即使 corePoolSize 为0,也会创建一个新线程。

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

JDK 1.7+实施(类似于JDK 1.6但具有更好的锁定和状态检查)

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);
}

即使<$ c,JDK 1.7也会创建一个新线程$ c> corePoolSize 为0。

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

所以,似乎 corePoolSize = 0 是JDK 1.5和JDK 1.6+的每个版本的特例。

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

但奇怪的是,该书的解释与任何节目结果都不符。

这篇关于具有corePoolSize 0的ThreadPoolExecutor在任务队列已满之前不应执行任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆