门户网站做seo,php手机网站开发工具,建设图书馆网站的意义,仿站小工具 wordpressThreadPoolExecutor 是 Java 标准库中用于创建和管理线程池的核心类之一。它实现了 ExecutorService 接口#xff0c;提供了丰富的线程池管理功能。下面将通过源码解析来深入了解 ThreadPoolExecutor 类的工作原理和各个重要部分。
可以在 Java 源代码中找到 ThreadPoolExecu…ThreadPoolExecutor 是 Java 标准库中用于创建和管理线程池的核心类之一。它实现了 ExecutorService 接口提供了丰富的线程池管理功能。下面将通过源码解析来深入了解 ThreadPoolExecutor 类的工作原理和各个重要部分。
可以在 Java 源代码中找到 ThreadPoolExecutor 类的实现位于 java.util.concurrent 包中。
以下是 ThreadPoolExecutor 类的一些关键概念和部分
1、构造函数
ThreadPoolExecutor 类提供了几个不同的构造函数允许传递核心线程数、最大线程数、空闲线程存活时间、任务队列等参数。这些参数决定了线程池的基本行为。 下面是几个常用的构造函数及其参数的解释
核心线程数 (corePoolSize)
核心线程数表示线程池中始终保持存活的线程数量。这些线程会一直存在即使它们处于空闲状态。线程池会根据任务数量自动创建新线程直到核心线程数达到上限。最大线程数 (maximumPoolSize)
最大线程数表示线程池中最多可以同时存在的线程数量。当核心线程数已满且任务队列也已满时线程池会创建新线程直到最大线程数达到上限。如果达到最大线程数后仍有更多任务到达根据饱和策略进行处理。空闲线程存活时间 (keepAliveTime)
空闲线程存活时间表示当线程池中的线程数超过核心线程数并且这些线程处于空闲状态时它们会被保留的时间。超过此时间后多余的空闲线程将被终止从而节省系统资源。时间单位 (unit)
时间单位用于指定空闲线程存活时间的单位可以是秒、毫秒等。任务队列 (workQueue)
任务队列用于存储等待执行的任务。线程池可以使用不同类型的队列如 BlockingQueue 的各种实现。任务队列决定了等待执行的任务数量。线程工厂 (threadFactory)
线程工厂用于创建新线程默认使用 Executors.defaultThreadFactory()。可以自定义线程工厂来创建线程从而指定线程的名称、优先级等属性。饱和策略 (handler)
当线程池和任务队列都已满新的任务到达时饱和策略定义了如何处理这些任务。线程池提供了几种内置的饱和策略如 AbortPolicy、CallerRunsPolicy、DiscardPolicy 以及 DiscardOldestPolicy。2、线程池状态
ThreadPoolExecutor 的内部维护了几种状态包括 RUNNING、SHUTDOWN、STOP、TIDYING 和 TERMINATED。线程池在不同的状态下会有不同的行为例如当调用 shutdown 方法时线程池会从 RUNNING 状态转变为 SHUTDOWN 状态不再接受新的任务。
RUNNING运行中
在 RUNNING 状态下线程池处于正常运行状态可以接受新任务并执行已提交的任务。在这个状态下核心线程数和非核心线程数都可以创建和执行任务。SHUTDOWN关闭中
当调用线程池的 shutdown 方法时线程池会进入 SHUTDOWN 状态。在这个状态下线程池不再接受新任务但会继续执行已提交的任务包括等待队列中的任务。STOP立即停止
当调用线程池的 shutdownNow 方法时线程池会进入 STOP 状态。在这个状态下线程池会尝试中断所有正在执行的线程并清空任务队列。TIDYING整理中
当线程池状态从 SHUTDOWN 转变为 TIDYING表示线程池已经停止接受新任务正在执行中的任务也已经完成处于整理和清理状态。在这个状态下线程池会执行一些清理操作例如中断空闲线程。TERMINATED已终止
当线程池状态从 TIDYING 转变为 TERMINATED表示线程池已经彻底终止所有任务都已执行完毕并且线程池中的所有线程都已销毁。在这个状态下线程池不再执行任何操作。3、任务队列
任务队列用于存储等待执行的任务。ThreadPoolExecutor 允许使用不同类型的队列如 BlockingQueue 的各种实现包括 LinkedBlockingQueue、ArrayBlockingQueue 等。这些队列控制了等待执行的任务数量。 以下是几种常见的任务队列类型及其特点
LinkedBlockingQueue链式阻塞队列
LinkedBlockingQueue 是一个基于链表的无界阻塞队列它可以存储无限数量的任务。在核心线程数未满的情况下新的任务会直接创建新线程来执行。当核心线程数已满时任务会被放入队列中等待执行。ArrayBlockingQueue数组阻塞队列
ArrayBlockingQueue 是一个基于数组的有界阻塞队列需要指定队列的容量。在核心线程数未满的情况下新的任务会直接创建新线程来执行。当核心线程数已满时任务会被放入队列中等待执行。PriorityBlockingQueue优先级阻塞队列
PriorityBlockingQueue 是一个无界阻塞队列它会根据任务的优先级来进行调度。具有较高优先级的任务会被优先执行。DelayedWorkQueue延迟工作队列
DelayedWorkQueue 是一个用于调度延迟任务的队列其中的任务可以设置延迟执行时间。适用于需要按照一定延迟执行任务的场景。SynchronousQueue同步队列
SynchronousQueue 是一个没有实际存储能力的队列每个插入操作必须等待一个相应的删除操作反之亦然。适用于需要实现一对一的任务交付机制。用了无界队列那非核心线程就不会创建了
是的当线程池使用无界队列如 LinkedBlockingQueue 或 PriorityBlockingQueue时非核心线程的创建将受到影响。在无界队列中任务可以无限制地排队等待执行因此不会触发创建额外的非核心线程。
对于无界队列以下是一些关键点需要注意
核心线程数 (corePoolSize) 核心线程数仍然会影响线程池的初始线程创建当任务提交到线程池时如果当前活动线程数小于核心线程数新任务会创建一个核心线程来执行。这适用于线程池的初始阶段或在任务数量较少的情况下。最大线程数 (maximumPoolSize) 如果任务的到达速率超过了核心线程的执行速度并且任务队列已满非核心线程将不会创建因为无界队列可以容纳所有任务。任务队列 无界队列会持续地存储等待执行的任务直到系统资源用尽。这可能导致内存占用逐渐增加因此在选择无界队列时需要注意系统资源的管理。无界队列适用于一些特定场景如任务量波动大任务执行时间差异较大或者希望尽量保留任务而不丢失的情况。然而需要注意的是如果任务数量持续增加无界队列可能会导致内存消耗过大因此在选择队列类型时需要综合考虑线程池的整体性能和资源利用。
阻塞队列
前面提到的 LinkedBlockingQueue、PriorityBlockingQueue 和 ArrayBlockingQueue 都是阻塞队列这里我将更详细地解释一下阻塞队列的概念以及它们的作用。
阻塞队列是一种特殊类型的队列具有以下特点
阻塞特性 当向队列添加元素或从队列中取出元素时如果队列已满或为空阻塞队列会自动阻塞线程直到队列变为非满或非空为止。线程安全 阻塞队列是线程安全的多个线程可以并发地进行入队和出队操作而不需要额外的同步措施。在 ThreadPoolExecutor 中任务队列是一个关键组件它决定了线程池中等待执行的任务数量、调度策略以及如何处理任务。不同类型的阻塞队列在不同的情况下有不同的用途和特点可以根据实际需求进行选择。
具体来说
LinkedBlockingQueue 是一个基于链表的无界阻塞队列。当任务数量超过核心线程数时新的任务会被放入队列中等待执行。如果队列已满新任务会阻塞等待直到有空间。
ArrayBlockingQueue 是一个基于数组的有界阻塞队列。它需要指定队列的容量。当队列已满时新的任务会阻塞等待直到有空间。
PriorityBlockingQueue 是一个无界阻塞队列根据元素的优先级来进行调度。优先级高的元素会被先出队执行。请注意阻塞队列适用于不同的场景和需求。根据应用特性和性能要求选择适合的阻塞队列类型是很重要的。
LinkedBlockingQueue无界队列?
LinkedBlockingQueue 是一个基于链表的可选界限阻塞队列而不是无界队列。这意味着它可以选择性地指定队列的容量当容量未指定时队列会默认为无界。
所以当使用 LinkedBlockingQueue 作为任务队列时如果没有指定容量或者容量为 Integer.MAX_VALUE队列会被认为是无界的新任务总是可以放入队列中而不会因为队列已满而阻塞。
如果指定了容量当任务数量超过容量时新的任务会被放入队列中等待执行。当队列已满时新任务会阻塞等待直到有空间这是典型的阻塞队列行为。
4、线程工厂
ThreadPoolExecutor 允许通过提供线程工厂来自定义线程的创建过程包括线程的名称、优先级、是否守护线程等属性。线程工厂负责创建新的线程实例然后线程池会使用这些线程来执行任务。
ThreadPoolExecutor 的构造函数中有一个参数 threadFactory可以传递一个实现了 ThreadFactory 接口的对象来指定线程工厂。ThreadFactory 接口只有一个方法 newThread用于创建新的线程。以下是一个简单的示例
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;public class CustomThreadFactoryExample {public static void main(String[] args) {ThreadFactory threadFactory new CustomThreadFactory(MyThreadGroup);ThreadPoolExecutor executor new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS,new LinkedBlockingQueue(), threadFactory);// ... 添加任务到线程池并执行 ...executor.shutdown();}
}class CustomThreadFactory implements ThreadFactory {private final ThreadGroup threadGroup;private final AtomicInteger threadNumber new AtomicInteger(1);public CustomThreadFactory(String threadGroupName) {SecurityManager s System.getSecurityManager();threadGroup (s ! null) ? s.getThreadGroup() :Thread.currentThread().getThreadGroup();}public Thread newThread(Runnable r) {Thread thread new Thread(threadGroup, r,MyThread- threadNumber.getAndIncrement(),0);if (thread.isDaemon())thread.setDaemon(false);if (thread.getPriority() ! Thread.NORM_PRIORITY)thread.setPriority(Thread.NORM_PRIORITY);return thread;}
}在上面的示例中创建了一个自定义的线程工厂 CustomThreadFactory 实现了 ThreadFactory 接口用于创建具有自定义属性的线程。在这个例子中指定了线程组、线程名称前缀等属性。
通过使用线程工厂可以为线程池中的每个线程设置特定的属性从而更好地管理和控制线程的行为。这对于在多线程应用程序中调试和监视线程非常有帮助。
5、饱和策略
当线程池和任务队列都已满新的任务到达时饱和策略定义了如何处理这些任务。ThreadPoolExecutor 提供了几种内置的饱和策略如 AbortPolicy抛出异常、CallerRunsPolicy由调用者线程执行任务、DiscardPolicy丢弃任务以及 DiscardOldestPolicy丢弃最旧的任务。 下面对每种策略进行详细解释
AbortPolicy抛出异常
这是默认的饱和策略。当线程池和队列都已满时新任务会导致 RejectedExecutionException 异常被抛出提示线程池已经饱和。这是一种保守的策略防止任务丢失。CallerRunsPolicy由调用者线程执行任务
当线程池和队列都已满时新任务会被调用者线程提交任务的线程直接执行而不会交给线程池中的线程来执行。这种策略可能会导致调用者线程阻塞因为它们会等待任务执行完毕。DiscardPolicy丢弃任务
当线程池和队列都已满时新任务会被直接丢弃不会进行任何处理。这可能会导致任务丢失慎用此策略。DiscardOldestPolicy丢弃最旧的任务
当线程池和队列都已满时会尝试将最早的任务从队列中移除然后添加新任务。这可能会导致一些旧任务被丢弃以便为新任务腾出空间。这些饱和策略提供了不同的处理方式可以根据应用需求来选择合适的策略。通常情况下AbortPolicy 是默认的且安全的选择因为它会在资源不足时抛出异常提示应该考虑调整线程池大小或处理任务队列。其他策略可能会导致任务丢失或阻塞需要根据具体情况谨慎选择。
6、线程池的执行过程
当任务提交给 ThreadPoolExecutor 后线程池会根据当前状态、核心线程数、任务队列状态等决定任务的处理方式。如果核心线程数未满会创建新线程来执行任务如果核心线程数已满会尝试将任务放入队列如果队列也已满会根据饱和策略来处理任务。
描述的流程如下
核心线程数未满
如果线程池的当前活动线程数小于核心线程数线程池会创建一个新的核心线程来立即执行提交的任务。核心线程数已满
如果线程池的当前活动线程数达到核心线程数新任务会被放入任务队列等待执行。队列未满
如果任务队列未满新任务会被放入队列中等待执行。队列已满
如果任务队列已满根据选择的饱和策略来处理任务。可能的策略包括抛出异常AbortPolicy如果饱和策略为 AbortPolicy则新任务会被拒绝并抛出 RejectedExecutionException 异常。由调用者线程执行CallerRunsPolicy如果饱和策略为 CallerRunsPolicy则提交任务的线程调用者线程会执行该任务而不会交给线程池中的线程执行。丢弃任务DiscardPolicy如果饱和策略为 DiscardPolicy则新任务会被直接丢弃不会进行任何处理。丢弃最旧的任务DiscardOldestPolicy如果饱和策略为 DiscardOldestPolicy则尝试从队列中移除最旧的任务以便为新任务腾出空间。线程池根据这些步骤来动态调整线程的创建和任务的处理以适应不同的并发情况和资源限制。这种灵活的处理方式使得线程池能够在不同的负载下保持高效的任务处理能力。
7、线程池的终止
调用 shutdown 方法会触发线程池的终止过程。线程池会拒绝新的任务等待已提交但未执行的任务完成然后关闭线程池中的线程。
调用 shutdown 方法是线程池的一种优雅关闭方式。下面将更详细地解释 shutdown 方法的作用和线程池的终止过程
调用 shutdown 方法
当调用线程池的 shutdown 方法时线程池会开始终止的过程。在此过程中线程池将不再接受新的任务但会继续执行已提交但尚未执行的任务同时等待队列中的任务也会被继续执行。任务执行和队列处理
在终止过程中线程池会让已经创建的核心线程和非核心线程继续处理已提交的任务。同时线程池也会尝试从任务队列中获取任务来执行。如果队列中还有等待执行的任务线程池会继续分配线程来执行这些任务。拒绝新任务
在调用 shutdown 方法后线程池会拒绝接受新的任务。任何尝试提交新任务的操作都会被拒绝并且会抛出 RejectedExecutionException 异常。等待任务完成
在终止过程中线程池会等待队列中的任务和正在执行的任务都完成。这意味着线程池不会立即关闭而是会等待任务全部执行完毕。关闭线程池中的线程
一旦所有任务都执行完毕线程池会关闭其中的线程。如果线程池中存在非核心线程它们在任务执行完毕后会根据 keepAliveTime 和空闲时间来判断是否终止。终止状态
当线程池中的所有线程都已关闭时线程池会达到 TERMINATED 状态表示线程池已经完全终止。总之调用 shutdown 方法后线程池会等待已提交但未执行的任务完成并且关闭线程池中的线程最终达到终止状态。这种方式可以避免任务丢失并且允许线程池逐步优雅地停止释放资源。