【问题标题】:when does executor really create new threadexecutor 什么时候真正创建新线程
【发布时间】:2026-01-17 17:10:01
【问题描述】:

我对什么时候创建新线程感到困惑,就像执行者一样。我的理解是:对于cachedthreadpool,是根据任务的提交来创建新的线程。至于fixedthreadpool和singlethread,不管任务的提交,线程都是提前创建的。是不是我的理解错了?

ExecutorService exec = Executors.newCachedThreadPool(); 
exec.execute(new LiftOff());

ExecutorService exec = Executors.newFixedThreadPool(3); // Are the three threads created 
                                                        // after this line executes?
exec.execute(new LiftOff());

ExecutorService exec = Executors.newSingleThreadExecutor(); // Is the single thread created 
                                                            // after this line executes?
exec.execute(new LiftOff());

【问题讨论】:

  • 您可以使用调试器或查看Executors 的代码来回答这些问题。
  • 这些方法中的每一个都有一个接受ThreadFactory的变体,你可以传入你自己的实现来记录何时请求线程。

标签: java multithreading


【解决方案1】:

简短的回答是,在您提到的所有情况下,仅当您开始执行任务时才会创建线程,即在执行此 ExecutorService exec = Executors.newFixedThreadPool(3); 行后不会创建线程,并且第一个线程将仅在执行此 exec.execute(new LiftOff()); 行后创建。


为了更好更全面地理解这一点,正如@Oliver 建议的那样,您需要浏览 Java 源代码并阅读 ThreadPoolExecutor,尤其是核心池大小、最大池大小等。如果您想快速理解这一点用外行的话来说,请阅读this good answer

现在,在我带您看一些示例代码之前,需要了解以下几点:

  • Java 的实用程序类Executors 将创建并返回ExecutorService 的对象(请注意ExecutorService 是一个接口)。
  • 现在在newCachedThreadPool()newFixedThreadPool(int nThreads) 的情况下,您将获得ThreadPoolExecutor 的对象(请注意ThreadPoolExecutor 实现ExecutorService 接口)。
  • 正如您在源代码中看到的,当您执行Executors.newFixedThreadPool(3); 时,您只会得到一个ThreadPoolExecutor 的对象,其中设置了所有实例变量,如核心池大小、最大池大小等,在您的情况下,它将设置为 3。
  • 此时没有创建线程,只有在您开始使用executesubmitinvokeAll 执行任务时才会创建真正的线程。
  • 正如您在源代码中看到的,当您第一次执行execute 时,如果当前池大小小于核心池大小,它将仅创建 1 个新线程,因此这是您的线程开始创建的地方。

请参阅下面的示例代码,这将帮助您理解并证明线程仅在您开始执行任务时创建。 解释一下,我做的主要技巧是我使用了ThreadPoolExecutor 的对象引用变量,这样我就可以调用像getPoolSize() 这样的方法(请注意getPoolSize() 告诉您当前有多少线程存在于池中),因为当您使用 ExecutorService 的对象引用变量时,您无法调用这些方法(我确信我不需要告诉“为什么”)。

当您运行此示例时,您会注意到

  • ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3); 之后没有创建线程
  • 在第一个 executeRunnable(poolExecutor); 之后,池中有一个线程。
  • 由于您使用的核心池大小为 3,因此在创建 3 个线程后不再创建线程。 (请阅读核心、最大池大小和“队列”大小)

package com.learn.*.multithreading;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 
 * @author himanshu.agrawal
 *
 */
public class ExecutorServiceNumberOfThreads {
    public static void main(String[] args) {
        System.out.println("### Testing Executors.newFixedThreadPool()");
        testNewFixedThreadPool();
    }

    private static void testNewFixedThreadPool() {
        ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
        System.out.println("Pool when no execute() : " + poolExecutor.getPoolSize());
        executeRunnable(poolExecutor);
        System.out.println("Pool after 1st execute() : " + poolExecutor.getPoolSize());
        executeRunnable(poolExecutor);
        System.out.println("Pool after 2nd execute() : " + poolExecutor.getPoolSize());
        executeRunnable(poolExecutor);
        System.out.println("Pool after 3rd execute() : " + poolExecutor.getPoolSize());
        executeRunnable(poolExecutor);
        System.out.println("Pool after 4th execute() : " + poolExecutor.getPoolSize());
        executeRunnable(poolExecutor);
        System.out.println("Pool after 5th execute() : " + poolExecutor.getPoolSize());
    }

    private static void executeRunnable(ThreadPoolExecutor poolExecutor) {
        poolExecutor.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("Running: " + Thread.currentThread().getId() + " | " + new Date());
            }
        });
    }

}

输出:

### Testing Executors.newFixedThreadPool()
Pool when no execute() : 0
Pool after 1st execute() : 1
Pool after 2nd execute() : 2
Pool after 3rd execute() : 3
Pool after 4th execute() : 3
Pool after 5th execute() : 3
Running: 10 | Sun Apr 23 19:50:32 IST 2017
Running: 8 | Sun Apr 23 19:50:32 IST 2017
Running: 10 | Sun Apr 23 19:50:32 IST 2017
Running: 9 | Sun Apr 23 19:50:32 IST 2017
Running: 8 | Sun Apr 23 19:50:32 IST 2017

【讨论】:

  • 很清楚。我通常阅读 Java 文档,但我认为阅读源代码对我来说很难。
  • 谢谢,没问题。许多专家表示,检查并尝试理解源代码是理解 Java 内容的最佳方式。