【问题标题】:ThreadPoolExecutor.execute(Runnable command) when to create a new threadThreadPoolExecutor.execute(Runnable command) 何时创建新线程
【发布时间】:2019-10-27 20:34:14
【问题描述】:

我正在阅读ThreadPoolExecutor.java的源码,下面是execute的方法:

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

假设线程池有2个核心线程,设置最大池大小为4。

我可以理解代码if (workerCountOf(c) &lt; corePoolSize) { addWorkder(..) },这意味着如果当前核心线程数小于核心轮询大小,只需创建一个新线程来处理可运行命令。

我无法理解的是,假设我们已经拨打了两次execute(runnable),而且他们每个人都需要很长时间才能完成,所以他们现在还在忙,现在我们正在拨打第三次电话。

代码会做什么?我认为代码转到if (isRunning(c) &amp;&amp; workQueue.offer(command)) {,因此命令被添加到工作队列中。但是,我不明白这第三条命令将由哪个线程执行。根据代码else if (workerCountOf(recheck) == 0),我认为工人数应该是2,因为我们已经添加了两个工人。

所以我的问题是什么时候添加第三个工人?

--编辑--

我的测试代码:


public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                2,
                4,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4)
        );

        threadPoolExecutor.execute(new Command("A"));
        threadPoolExecutor.execute(new Command("B"));
        threadPoolExecutor.execute(new Command("C"));

    }

    static class Command implements Runnable {
        private String task;
        Command(String task) {
            this.task = task;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(1000 * 10);
                System.out.println(new Date() + " - " + Thread.currentThread().getName() + " : " + task);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

打印出来:

Thu Jun 13 17:44:30 CST 2019 - pool-1-thread-1 : A
Thu Jun 13 17:44:30 CST 2019 - pool-1-thread-2 : B
Thu Jun 13 17:44:40 CST 2019 - pool-1-thread-1 : C

使用测试代码,我希望核心工作人员保持忙碌 10 秒,所以当execute("C") 我想打“核心工作人员很忙,将添加第三个工作人员”的情况时,但似乎有没有第三个工人?抱歉,怎么了?

谢谢。

【问题讨论】:

  • “代码会做什么?”我觉得调试器比我们能解释得更好……
  • @AndrewTobilko 是的,我正在尝试
  • @AndrewTobilko 用调试器调试线程调度代码真的很困难。极端的海森堡效应。
  • 只有在现有的两个工作人员仍然忙碌时才会添加第三个工作人员。否则其中一人将接手任务。
  • @Thilo 感谢您指出Otherwise one of them will pick up the task。我想我可以理解核心工作线程有一个循环从工作队列中获取工作,因此workQueue.offer(command) 将启动其中一个,他们将处理可运行的。但是,我仍然无法通过阅读代码了解何时/如何添加第三名工人。谢谢。

标签: java multithreading threadpoolexecutor


【解决方案1】:

我想打一下'核心工作人员很忙,将添加第三名工作人员'

那你还得把队列填满。

Javadoc 说:

当在方法 execute(java.lang.Runnable) 中提交了一个新任务,并且运行的线程少于 corePoolSize 时,即使其他工作线程处于空闲状态,也会创建一个新线程来处理该请求。如果运行的线程数多于 corePoolSize 但少于 maximumPoolSize,只有在队列已满时才会创建一个新线程

【讨论】:

  • threadPoolExecutor.execute(new Command("A")); threadPoolExecutor.execute(new Command("B")); threadPoolExecutor.execute(new Command("C")); threadPoolExecutor.execute(new Command("D")); threadPoolExecutor.execute(new Command("E")); threadPoolExecutor.execute(new Command("F")); threadPoolExecutor.execute(new Command("G")); 这将创建第三个工作线程。 A / B 不会添加到工作队列。 C/D/E/F 填满了工作队列,所以在执行 G 的时候会碰到逻辑else if (!addWorker(command, false))
  • 我不得不说我也认为它会先添加工人然后排队。但显然不是。在您不能再排队之前,您将保持核心工作人员的规模。
【解决方案2】:

假设

   N = no of thread in currently in the pool.
   C = core size of pool
   M = maximum size of pool.
   BQ = Bounded Blocking Queue.(having a predefined capacity).
   UQ = Unbounded Blocking Queue.(without a predefined capacity).
   DHQ = Direct hand-offs Queue.

然后

       1. If BQ

           A. If N <= C , then thread always created when task is submitted, idle 
              thread is  present in pool or not doesn't matter.
           B. Once the core pool size is reached, executor start puting 
              the new task in queue if there is no idle thread. 
              If there is any idle thread then the task is assigned to idle thread.
           C. When BQ is full, then executor start creating again new thread till 
              its value reached to M if there is no idle thread.
              So the new thread creation after reaching N=C value is start when queue 
              is full.
           D. Once N=M reached and BQ is also full , then executor not accept any 
              task.It throw exception.

      2. If UQ 

           A. Same as above
           B. Same as above
           C. Not applicable. Why ? because it is unbounded queue.
              (UQ capacity is Integer.MAX_VALUE)
           D. No effect of M. Why ? 
              Since creation of new thread again is start after the queue is full,but 
              in the case UQ queue is never full. 
              So new thread never created once reach N=C for the new task submitted. 
              Means thread in thread pool always be equal to C (N=C always) in case 
              UQ , whatever the value of M


     3. If DHQ 
          A. The direct hand-offs queue never put the task in queue, its immediately
             assigned task to thread if any thread is idle ,if not then it create new 
             one.(task in queue is always 0)
          B. The concept of C is not applicable in this queue.Thread created till 
             its value reach M.
          C. Once the N value reach M (N=M), and try to submit the task ,it reject 
             the task.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 2012-02-20
    • 2018-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多