Skip to content

线程池

核心概念: 线程复用,任务调度,资源管理

  • 线程复用: 线程池通过维护一个线程集合,避免了频繁创建和销毁线程的开销,降低资源消耗提高系统响应速度
  • 任务调度: 线程池统一管理和调度提交的任务,将任务放入阻塞队列中,并由工作线程从队列中取出任务执行,实现了任务的生产者-消费者模式
  • 资源管理: 线程池可以限制线程的最大数量,防止无限制创建线程导致系统资源耗尽,提高线程的可管理性

关键事实

  • 线程池的优势: 降低资源消耗提高响应速度提高线程可管理性
  • ThreadPoolExecutor: Java 线程池的核心实现类,提供了更灵活和强大的线程池配置,允许自定义核心线程数最大线程数线程存活时间工作队列拒绝策略等参数。
  • ThreadPoolExecutor 的参数:
    • corePoolSize: 核心线程数,线程池基本保持的线程数量
    • maximumPoolSize: 最大线程数,线程池允许的最大线程数量
    • keepAliveTime: 线程存活时间非核心线程空闲时的最大存活时间,超过这个时间会被回收。
    • workQueue: 工作队列,用于存储等待执行的任务,常见的类型有 ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue 等。
    • RejectedExecutionHandler: 拒绝策略,当任务队列已满且线程数达到最大值时,用于处理新提交的任务,Java 提供了四种默认策略 (AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy, DiscardPolicy),也支持自定义。
  • 常见线程池类型 (Executors 工厂方法)
    • newFixedThreadPool: 固定大小线程池,核心线程数和最大线程数相等,使用无界队列 LinkedBlockingQueue,适用于任务量较稳定,需要保证响应速度的场景。缺点:可能导致 OOM (OutOfMemoryError),因为无界队列可能堆积大量请求。
    • newSingleThreadExecutor: 单线程线程池只有一个核心线程,使用无界队列 LinkedBlockingQueue,保证任务顺序执行缺点:同 newFixedThreadPool,可能导致 OOM
    • newCachedThreadPool: 可缓存线程池,核心线程数为 0,最大线程数无上限 (Integer.MAX_VALUE),使用同步队列 SynchronousQueue,线程空闲超过一定时间 (keepAliveTime) 会被回收,适用于任务数量波动大,但每个任务执行时间较短的场景。缺点:可能创建大量线程,导致 OOM.
  • 关闭线程池:
    • shutdown(): 平缓关闭,线程池不再接收新任务,但会继续执行完已提交的任务.
    • shutdownNow(): 立即关闭,线程池立即停止接收新任务,并尝试中断正在执行的任务,返回未执行的任务列表.
  • FutureTask: 用于封装 Callable 或 Runnable 任务,可以获取异步任务的执行结果 (get() 方法会阻塞等待结果),并监控任务状态 (isDone(), isCancelled()) 和取消任务 (cancel()).
  • submit 方法: 向线程池提交任务,可以提交 RunnableCallable 类型的任务,并返回一个 Future 对象,用于获取任务执行状态和结果submit(Runnable) 返回 Future<?>submit(Callable) 返回 Future<T>submit(Runnable, T result) 可以指定 Runnable 任务完成后的返回值.
  • ThreadPoolExecutor 内部状态: 通过 ctl 原子变量同时存储线程池运行状态 (runState)工作线程数量 (workerCount),实现了高效的状态管理. 线程池状态包括 RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED 等.
  • 任务执行原理 (execute 方法):
    1. 检查核心线程数: 若当前线程数小于 corePoolSize,则创建新线程执行任务.
    2. 尝试加入工作队列: 若当前线程数已达到 corePoolSize,则尝试将任务加入 workQueue.
    3. 检查最大线程数: 若工作队列已满,且当前线程数小于 maximumPoolSize,则创建新线程执行任务.
    4. 拒绝执行: 若工作队列已满,且当前线程数已达到 maximumPoolSize,则执行拒绝策略 (RejectedExecutionHandler).

总结与延伸

线程池是 Java 并发编程中管理和控制线程的重要机制,合理使用线程池可以显著提高多线程程序的性能和资源利用率. ThreadPoolExecutor 提供了丰富的配置选项,可以根据不同的应用场景进行精细化调优. 在实际开发中,应避免使用 Executors 工厂方法创建线程池,而是手动创建 ThreadPoolExecutor 实例,并根据任务特性和系统资源合理配置线程池参数,例如:CPU 密集型任务可设置较小的线程数 (Ncpu+1),IO 密集型任务可设置较大的线程数 (Ncpu2) 或更多. 同时,应尽量使用有界队列,并自定义拒绝策略,防止线程池资源耗尽,保证系统的稳定性和可靠性. 监控线程池的状态 (例如:getTaskCount(), getActiveCount(), getCompletedTaskCount()) 也是非常重要的,可以帮助我们了解线程池的运行状况*,及时发现和解决潜在问题.

为什么要有线程池?

线程池的出现是为了解决以下几个主要问题,并提供更高效、更可控的线程管理方式:

  1. 减少线程创建和销毁的开销:

    • 线程生命周期开销: 线程的创建和销毁是比较昂贵的操作,涉及到操作系统资源的分配和回收. 频繁地创建和销毁线程会消耗大量的系统资源,降低程序性能.
    • 线程池复用: 线程池通过复用已创建的线程来执行新的任务,避免了线程创建和销毁的开销,提高了程序的响应速度和执行效率.
    • 提高系统资源的利用率:

    • 按需创建: 线程池可以根据实际任务需求动态地创建和管理线程,避免了创建过多线程导致系统资源浪费,或者线程不足导致任务堆积.

    • 资源限制: 线程池可以限制线程的最大数量,防止无限制地创建线程导致系统资源耗尽,保证系统的稳定性.
    • 提高程序的响应速度:

    • 任务排队: 当有新任务到达时,线程池可以立即从线程池中获取空闲线程来执行任务,或者将任务放入队列中等待执行,而不是等待新的线程创建完成. 这缩短了任务的等待时间,提高了程序的响应速度.

    • 并发处理能力: 线程池可以并发执行多个任务,提高了程序的整体处理能力和吞吐量.
    • 提供更方便的线程管理和监控:

    • 统一管理: 线程池可以统一管理线程的创建、销毁、调度和监控,简化了并发编程的复杂性.

    • 监控和调优: 线程池提供了监控线程池状态 (例如,活跃线程数、队列长度、已完成任务数等) 的接口,方便开发者了解线程池的运行状况,并进行性能调优.

总结: 线程池的核心价值在于线程复用资源管理,它可以显著降低线程管理的开销,提高系统资源的利用率,并提升程序的响应速度和并发性能.

Java 中实现和管理线程池有哪些方式? 请简单举例如何使用。

Java 中实现和管理线程池主要通过 java.util.concurrent 包下的 ExecutorService 接口及其实现类 ThreadPoolExecutorScheduledThreadPoolExecutor.

1. 使用 Executors 工厂类 (简化方式):

Executors 类提供了一系列静态工厂方法,可以快速创建几种常用的线程池:

  • Executors.newFixedThreadPool(int nThreads): 创建固定大小的线程池. 核心线程数和最大线程数都为 nThreads,使用 LinkedBlockingQueue 作为任务队列 (无界队列).

  • Executors.newCachedThreadPool(): 创建可缓存的线程池. 核心线程数为 0,最大线程数为 Integer.MAX_VALUE,使用 SynchronousQueue 作为任务队列 (直接提交队列). 线程空闲超过 60 秒会被回收.

  • Executors.newSingleThreadExecutor(): 创建单线程的线程池. 核心线程数和最大线程数都为 1,使用 LinkedBlockingQueue 作为任务队列 (无界队列). 保证所有任务按照提交顺序依次执行.

  • Executors.newScheduledThreadPool(int corePoolSize)Executors.newSingleThreadScheduledExecutor(): 创建定时周期性执行任务的线程池 (ScheduledExecutorService 接口).

2. 使用 ThreadPoolExecutor 类 (更灵活、更精细的控制):

直接使用 ThreadPoolExecutor 类可以更灵活地配置线程池的各种参数,例如核心线程数、最大线程数、线程空闲时间、任务队列、拒绝策略等.

import java.util.concurrent.*;

public class ThreadPoolExecutorExample {
    public static void main(String[] args) {
        int corePoolSize = 2;
        int maxPoolSize = 5;
        long keepAliveTime = 60L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100); // 有界队列
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maxPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                rejectedExecutionHandler
        );

        for (int i = 0; i < 15; i++) {
            final int taskIndex = i;
            threadPoolExecutor.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " is running task " + taskIndex);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        threadPoolExecutor.shutdown();
    }
}

总结: Executors 工厂类提供了快速创建常用线程池的便捷方式,而 ThreadPoolExecutor 类则提供了更精细的配置和控制能力. 选择哪种方式取决于具体的应用场景和需求.

为什么很多公司不允许使用 Executors 去创建线程池? 那么推荐怎么使用呢?

虽然 Executors 工厂类提供了创建线程池的便利性,但很多公司在代码规范中会禁止不推荐使用 Executors 创建线程池,主要原因在于 Executors 创建的某些线程池存在潜在的资源耗尽风险,以及缺乏足够的控制和监控能力.

Executors 创建线程池的潜在问题:

  1. FixedThreadPoolSingleThreadExecutor 使用无界队列 LinkedBlockingQueue:

    • OOM 风险: LinkedBlockingQueue无界队列,这意味着当所有核心线程都在忙碌时,新提交的任务会无限地加入到队列中等待. 如果任务提交速度远超于任务处理速度,队列可能会无限膨胀,最终导致 OOM (OutOfMemoryError) 内存溢出.
    • 资源耗尽: 虽然理论上是无界,但实际上受限于 JVM 堆内存大小. 当大量任务堆积在队列中时,会占用大量内存资源,可能导致系统性能下降甚至崩溃.
    • CachedThreadPool 使用 SynchronousQueue 和过多的最大线程数:

    • 线程数量失控: CachedThreadPool 的最大线程数设置为 Integer.MAX_VALUE,几乎是无限制的. 在高并发场景下,如果任务提交速度很快,CachedThreadPool 可能会快速创建大量线程,超出系统承受能力,导致 CPU 飙升系统 load 过高、甚至 系统崩溃.

    • 频繁线程创建销毁: CachedThreadPool 的线程空闲时间较短 (60 秒),如果任务执行时间短且频率高,会导致频繁地创建和销毁线程,增加系统开销,降低性能.

推荐使用 ThreadPoolExecutor 手动创建线程池:

为了避免 Executors 创建线程池的潜在问题,并获得更精细的控制,推荐直接使用 ThreadPoolExecutor 类手动创建线程池.

手动创建 ThreadPoolExecutor 的优势:

  • 可配置有界队列: 可以根据实际需求选择有界队列 (例如 ArrayBlockingQueue, LinkedBlockingQueue (指定容量)),限制任务队列的长度,防止任务无限堆积导致 OOM.
  • 可配置拒绝策略: 可以根据实际需求选择合适的拒绝策略 (例如 AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy),处理当队列满且线程数达到最大值时新提交的任务,避免任务丢失或系统异常.
  • 更精细的参数调优: 可以根据应用的特点和性能需求,灵活地配置核心线程数、最大线程数、线程空闲时间、线程工厂等参数,进行更精细的性能调优.
  • 更强的监控能力: ThreadPoolExecutor 提供了更丰富的监控接口,可以方便地获取线程池的各种运行状态指标,进行更全面的监控和分析.

总结: Executors 工厂类提供的线程池虽然方便易用,但在某些场景下存在资源耗尽和控制不足的风险. 推荐在生产环境中使用 ThreadPoolExecutor 手动创建线程池,并根据实际需求合理配置各项参数,以确保线程池的稳定性和性能.

ThreadPoolExecutor 有哪些核心的配置参数? 请简要说明

ThreadPoolExecutor 的核心配置参数决定了线程池的行为和性能,主要包括以下几个:

  1. corePoolSize (核心线程数):

    • 含义: 线程池常驻的线程数量,即使这些线程处于空闲状态,也不会被回收 (除非设置了 allowCoreThreadTimeOut).
    • 作用: 保证线程池基本的并发处理能力. 当有新任务提交时,线程池会优先使用核心线程池中的线程来执行任务.
    • 设置建议: 根据CPU 核心数任务类型 (CPU 密集型或 IO 密集型) 来设置. 对于 CPU 密集型任务,可以设置为 CPU 核心数; 对于 IO 密集型任务,可以适当增加.
    • maximumPoolSize (最大线程数):

    • 含义: 线程池最多可以创建的线程数量. 当任务队列满了,且当前线程数小于最大线程数时,线程池会创建新的线程来执行任务.

    • 作用: 应对突发的高并发请求,提高线程池的峰值处理能力.
    • 设置建议: 应该大于等于核心线程数. 设置过大可能会导致系统资源耗尽,设置过小可能无法充分利用系统资源. 需要根据系统资源任务特性综合考虑.
    • keepAliveTime (线程空闲时间):

    • 含义: 当线程池中线程数量超过核心线程数时,空闲线程在等待新任务的最长存活时间. 超过这个时间,空闲线程会被回收,直到线程池的线程数减少到核心线程数.

    • 作用: 动态调整线程池的线程数量,在任务低峰期回收多余的线程,减少资源消耗.
    • 单位: 需要配合 unit 参数指定时间单位 (例如 TimeUnit.SECONDS, TimeUnit.MILLISECONDS).
    • 设置建议: 根据任务的平均执行时间任务的到达频率来设置. 如果任务执行时间短且频率高,可以设置较短的空闲时间; 如果任务执行时间长且频率低,可以设置较长的空闲时间.
    • unit (时间单位):

    • 含义: keepAliveTime 参数的时间单位. 例如 TimeUnit.SECONDS, TimeUnit.MILLISECONDS, TimeUnit.MINUTES 等.

    • 作用: 指定 keepAliveTime 的时间单位.
    • workQueue (任务队列):

    • 含义: 用于缓存等待执行的任务的队列. 当核心线程都在忙碌时,新提交的任务会先进入任务队列排队等待.

    • 类型: 常见的任务队列类型包括:
      • ArrayBlockingQueue: 有界数组队列,FIFO. 容量固定,可以防止 OOM,但可能导致任务拒绝.
      • LinkedBlockingQueue: 有界/无界链表队列,FIFO. 容量可以动态增长 (无界时),但可能导致 OOM (无界时).
      • SynchronousQueue: 直接提交队列,不缓存任务. 每个插入操作必须等待一个移除操作,反之亦然. 适用于线程数可以动态增长的场景 (例如 CachedThreadPool).
      • PriorityBlockingQueue: 无界优先级队列. 任务会根据优先级排序,优先级高的任务先执行.
    • 作用: 缓冲任务,平衡生产者和消费者的速度差异,控制任务的排队策略.
    • 设置建议: 根据任务的特性系统资源来选择合适的队列类型和容量. 对于有界队列,需要权衡队列容量大小与任务拒绝策略. 对于无界队列,需要注意 OOM 风险.
    • threadFactory (线程工厂):

    • 含义: 用于创建新线程的工厂. 可以自定义线程的创建方式,例如设置线程名称、是否守护线程、线程优先级等.

    • 默认实现: Executors.defaultThreadFactory() 提供默认的线程工厂实现.
    • 作用: 定制化线程的创建过程. 例如,可以为线程设置有意义的名称,方便线程池的监控和问题排查.
    • rejectedExecutionHandler (拒绝策略):

    • 含义: 当任务队列已满且线程池线程数已达到最大线程数时,新提交的任务会被拒绝执行. rejectedExecutionHandler 定义了如何处理被拒绝的任务.

    • 常见策略: 包括 AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy (详细解释见下文).
    • 作用: 控制线程池在超负荷情况下的行为,保护系统资源,保证系统的稳定性.
    • 设置建议: 根据业务场景对任务丢失的容忍度来选择合适的拒绝策略.

总结: ThreadPoolExecutor 的核心参数共同决定了线程池的线程数量、任务队列、线程回收策略、以及任务拒绝策略. 合理配置这些参数是构建高效、稳定的线程池的关键.

ThreadPoolExecutor 可以创建哪是哪三种线程池呢?

虽然 ThreadPoolExecutor 本身是一个类,而不是 "创建三种线程池" 的工厂方法,但我们通常会根据 ThreadPoolExecutor 的不同配置参数组合,来模拟 Executors 工厂类创建的三种常用线程池类型,以及一些其他的变体. 这三种 "模拟" 的线程池类型分别是:

  1. FixedThreadPool (固定大小线程池):

    • 模拟配置:
      • corePoolSize = maximumPoolSize = nThreads (指定的线程数)
      • workQueue = LinkedBlockingQueue (无界队列)
    • 特点: 线程池中线程数量固定不变,始终保持 nThreads 个线程. 使用无界队列,任务队列可以无限增长 (理论上).
    • 适用场景: 适用于任务量比较稳定,需要限制并发线程数的场景. 例如,服务器连接处理、固定数量的任务并行处理等.
    • 风险: 使用无界队列,可能导致 OOM 风险.
    • CachedThreadPool (可缓存线程池):

    • 模拟配置:

      • corePoolSize = 0 (核心线程数为 0)
      • maximumPoolSize = Integer.MAX_VALUE (最大线程数无限制)
      • keepAliveTime = 60 秒 (线程空闲时间 60 秒)
      • workQueue = SynchronousQueue (直接提交队列)
    • 特点: 线程池初始线程数为 0,当有新任务提交时,如果线程池中有空闲线程,则复用空闲线程; 如果没有空闲线程,则创建新的线程. 线程空闲超过 60 秒会被回收. 使用直接提交队列,任务不会排队等待,而是直接提交给线程执行.
    • 适用场景: 适用于任务执行时间短任务数量突发性高的场景. 例如,处理大量短连接请求.
    • 风险: 最大线程数无限制,可能导致线程数量失控,系统资源耗尽.
    • SingleThreadExecutor (单线程线程池):

    • 模拟配置:

      • corePoolSize = maximumPoolSize = 1 (核心线程数和最大线程数都为 1)
      • workQueue = LinkedBlockingQueue (无界队列)
    • 特点: 线程池中只有一个线程,所有提交的任务都按照 FIFO 顺序依次执行. 保证任务的顺序执行. 使用无界队列,任务队列可以无限增长 (理论上).
    • 适用场景: 适用于需要顺序执行任务的场景,例如,单线程任务调度、消息队列的顺序消费等.
    • 风险: 使用无界队列,可能导致 OOM 风险.

总结: ThreadPoolExecutor 通过不同的参数配置,可以灵活地创建各种类型的线程池,以适应不同的并发场景需求. 理解这些参数的含义和组合方式,可以帮助我们更好地使用线程池.

当队列满了并且 worker 的数量达到 maxSize 的时候,会怎么样?

当线程池的任务队列 (workQueue) 已满,并且当前工作线程数 (workerCount) 已经达到最大线程数 (maximumPoolSize) 时,如果再有新的任务提交到线程池,线程池会执行拒绝策略 (rejectedExecutionHandler) 来处理这些无法执行的新任务.

拒绝策略的触发条件:

  • 任务队列已满: workQueue.offer(task) 返回 false,表示队列已满,无法再添加新任务.
  • 工作线程数已达最大值: workerCount >= maximumPoolSize,表示线程池已经创建了最大数量的线程,无法再创建新的线程.

拒绝策略的处理方式:rejectedExecutionHandler 决定,常见的策略包括 (详细解释见下文):

  • AbortPolicy (默认策略): 直接抛出 RejectedExecutionException 异常,终止任务执行,调用者可以捕获异常进行处理.
  • CallerRunsPolicy: 由提交任务的线程 (调用者线程) 自己执行被拒绝的任务. 降低了任务提交速度,但保证了任务不会被丢失.
  • DiscardPolicy: 直接丢弃被拒绝的任务,不抛出异常,也不做任何处理. 任务会被默默地丢弃,可能导致数据丢失.
  • DiscardOldestPolicy: 丢弃队列中队首 (最旧) 的任务,然后尝试重新提交当前被拒绝的任务. 为新任务腾出空间,但可能导致旧任务丢失.

总结: 当线程池超负荷运行时,拒绝策略是保护线程池和系统资源的重要机制. 选择合适的拒绝策略需要根据具体的业务场景和对任务丢失的容忍度来权衡.

说说 ThreadPoolExecutor 有哪些 RejectedExecutionHandler 策略? 默认是什么策略?

ThreadPoolExecutor 提供了四种内置的 RejectedExecutionHandler 策略,用于处理被拒绝的任务. 开发者也可以自定义 RejectedExecutionHandler 策略.

内置的 RejectedExecutionHandler 策略:

  1. AbortPolicy (默认策略):

    • 行为: 当任务被拒绝时,AbortPolicy抛出 RejectedExecutionException 异常.
    • 特点: 立即终止任务执行,抛出异常通知调用者任务被拒绝. 是 ThreadPoolExecutor默认拒绝策略.
    • 适用场景: 适用于不允许任务丢失的场景,希望及时发现和处理任务被拒绝的情况. 调用者可以捕获 RejectedExecutionException 异常,并进行相应的处理 (例如,重试任务、记录日志、报警等).
    • CallerRunsPolicy:

    • 行为: 当任务被拒绝时,CallerRunsPolicy由提交任务的线程 (调用者线程) 自己同步执行被拒绝的任务.

    • 特点: 不会抛出异常不会丢弃任务,而是将任务退回给调用者线程执行. 降低了任务提交速度,相当于将一部分任务处理压力转移给调用者线程.
    • 适用场景: 适用于不希望任务被丢弃,并且允许降低任务提交速度的场景. 例如,流量削峰、任务降级处理等.
    • DiscardPolicy:

    • 行为: 当任务被拒绝时,DiscardPolicy直接丢弃被拒绝的任务,不做任何处理,也不抛出异常.

    • 特点: 默默丢弃任务,不通知调用者不抛出异常. 简单粗暴,但可能导致数据丢失.
    • 适用场景: 适用于允许任务丢失的场景,例如,日志记录 (可以容忍少量日志丢失)、某些监控数据采集等.
    • DiscardOldestPolicy:

    • 行为: 当任务被拒绝时,DiscardOldestPolicy丢弃队列中队首 (最旧) 的任务,然后尝试重新提交当前被拒绝的任务.

    • 特点: 丢弃旧任务为新任务腾出空间尝试执行新任务. 一定程度上保证了新任务的执行,但可能导致旧任务丢失.
    • 适用场景: 适用于希望优先处理新任务,可以容忍丢弃旧任务的场景. 例如,缓存更新 (优先更新最新的数据)、某些实时性要求较高的任务处理等. 注意: DiscardOldestPolicy 需要配合有界队列使用,否则可能会一直丢弃队首任务,导致队列为空.

默认拒绝策略: ThreadPoolExecutor默认拒绝策略是 AbortPolicy.

自定义拒绝策略: 开发者可以实现 RejectedExecutionHandler 接口,自定义更复杂的拒绝策略,例如:

  • 记录日志: 将拒绝的任务信息记录到日志中,方便后续分析和处理.
  • 报警通知: 当任务被拒绝时,发送报警通知 (例如,邮件、短信) 给运维人员.
  • 持久化存储: 将被拒绝的任务持久化存储到数据库或消息队列中,稍后重试执行.

选择建议: 选择合适的拒绝策略需要根据具体的业务场景和需求来权衡. AbortPolicy (默认策略) 适用于需要及时发现和处理任务拒绝的情况; CallerRunsPolicy 适用于不希望任务被丢弃,可以降低任务提交速度的场景; DiscardPolicy 适用于允许任务丢失的场景; DiscardOldestPolicy 适用于希望优先处理新任务的场景. 自定义拒绝策略可以提供更灵活的处理方式.

简要说下线程池的任务执行机制? execute –> addWorker –> runWorker (getTask)

ThreadPoolExecutor 的任务执行机制可以用以下简要流程概括:

  1. execute(Runnable command) (任务提交):

    • 当客户端代码调用 threadPoolExecutor.execute(Runnable task) 方法提交任务时,线程池开始执行任务提交流程.
    • addWorker(Runnable firstTask, boolean core) (添加 Worker 线程):

    • execute() 方法内部会调用 addWorker() 方法来尝试添加 Worker 线程来执行任务.

    • Worker 线程: WorkerThreadPoolExecutor 的内部类,它实现了 Runnable 接口,负责执行任务. 每个 Worker 对象都持有一个线程 (Thread),并维护着一个任务队列 (workQueue) 的引用.
    • 添加 Worker 的条件判断: addWorker() 方法会根据以下条件判断是否需要添加新的 Worker 线程:
      • 线程池状态: 线程池必须