深入解剖线程池(ThreadPoolExecutor)

news/2025/2/14 5:08:26/

目录

  • 1 线程池(ThreadPoolExecutor)
  • 2 线程池的使用(Executors)
    • 2.1 newFixedThreadPool
    • 2.2 newCachedThreadPool
    • 2.3 newSingleThreadExecutor
    • 2.4 newScheduledThreadPool

多线程应用程序中,线程的创建和销毁开销相对较高。每次创建线程都需要操作系统分配资源,销毁线程也需要释放资源。线程池就是为了解决这个问题,如果某个线程执行完自己的“任务”之后,并不是将线程释放,而是放到一个“池子”中,下次如果需要用到线程继续执行任务的话直接从池子中取,这样就不用再去重复的创建销毁。

1 线程池(ThreadPoolExecutor)

Java中线程池的底层真正实现是通过ThreadPoolExecutor来实现的,在使用的时候提供了Executors类供开发者使用,并提供了一系列的功能线程池。所以先来了解ThreadPoolExecutor的底层代码,然后再来分析Executors。

通过追源码,ThreadPoolExecutor的构造方法如下:

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, defaultHandler);}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);}public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;}

这是官方API文档的描述
在这里插入图片描述可以看到构造方法中有以下这些参数,参数有的是必需的,有的是可选的,现在理解一下这些参数:

必需参数

  • corePoolSize - 即使空闲时仍保留在池中的线程数(核心线程数),除非设置allowCoreThreadTimeOut
  • maximumPoolSize - 池中允许的最大线程数
  • keepAliveTime -当线程数大于核心时,多余的空闲线程在终止之前等待新任务的最大时间
  • unit - keepAliveTime参数的时间单位
  • workQueue - 在执行任务之前用于保存任务的队列。该队列将仅保存execute方法提交的Runnable任务,采用阻塞队列实现。(这里使用的阻塞式队列有有界和无界两种形式,对于有界阻塞队列来说,达到饱和的时候就会触发拒绝策略,但是当使用的是无界队列的时候就永远不可能“满”也就不会触发拒绝策略。)

可选参数

  • threadFactory:线程工厂。用于指定为线程池创建新线程的方式。
  • handler :拒绝策略。当达到最大线程数时需要执行的饱和策略。

当线程池的线程数达到最大线程数时,需要执行拒绝策略。
Executors 为我们实现了 4 种拒绝策略:

  1. AbortPolicy():超过负荷,直接抛出异常(默认策略).
  2. CallerRunsPolicy():调用者负责处理.
  3. DiscardOldestPolicy():丢弃队列中最早的任务,将新任务加入.
  4. DiscardPolicy():丢弃新来的任务.

简单描述一下线程池的工作原理,对上面的参数有具体的认识:
在这里插入图片描述代码示例

public class Demo1 {public static void main(String[] args) {//创建线程池(传入参数)ExecutorService pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS,new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());//向线程池提交任务for (int i = 0; i < 3; i++) {pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});}}
}

由于线程大于了线程池最大线程数,可以看到代码触发了拒绝策略,我们使用的是AbortPolicy(),所以超负荷就直接抛出异常。将循环中的3减少为2 就不会抛出异常。
在这里插入图片描述

2 线程池的使用(Executors)

Java对ThreadPoolExecutors进行了封装,方便使用者对线程池进行使用,Executors根据不同的使用场景提供了下面四种创建线程池的方式:

  • newFixedThreadPool: 创建固定线程数的线程池 定长线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池. 可缓存线程池
  • newSingleThreadExecutor: 创建只包含单个线程的线程池. 单线程化线程池
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令, 是进阶版的 Timer.定时线程池。

具体地

2.1 newFixedThreadPool

  • 源码:
    在这里插入图片描述在这里插入图片描述
  • 特点:
    固定大小的线程池
    包含核心线程和最大线程数都为n,即corePoolSize和maximumPoolSize均为n。
    阻塞队列没有大小限制。(这里对于keppalivetime参数为0的设置,我看见有很多博客将这里会对线程执行完任务后立马回收停止,我认为是不正确的,官方API对于这个参数的解释是
    在这里插入图片描述当线程数大于核心时,多余的空闲线程在终止之前等待新任务的最大时间,。默认情况下,线程池只会回收非核心线程,如果希望核心线程也要回收,可以设置allowCoreThreadTimeOut这个属性为true,一般情况下我们不会去回收核心线程。因为线程池本身就是实现线程的复用,而且这些核心线程在没有任务要处理的时候是处于阻塞状态并没有占用CPU资源。
  • 使用场景:适用于需要限制并发线程数量的场景,例如控制同时执行的线程数量,以避免资源耗尽。

2.2 newCachedThreadPool

  • 源码
    在这里插入图片描述

  • 特点:
    动态大小的线程池。
    没有核心线程,核心线程数为0,最大线程数为Integer.MAX_VALUE。
    使用SynchronousQueue作为阻塞队列,可无限扩展。
    非核心线程的存活时间为60秒,即空闲60秒后的线程将被终止。

  • 使用场景:
    适用于需要处理大量短时任务的情况,例如任务执行时间不定,需要动态分配线程。

2.3 newSingleThreadExecutor

  • 源码
    在这里插入图片描述

  • 特点:
    包含单一核心线程的线程池。
    使用LinkedBlockingQueue作为阻塞队列。

  • 使用场景:

    适用于需要顺序执行任务的情况,保证任务按照提交的顺序依次执行

2.4 newScheduledThreadPool

  • 源码
    在这里插入图片描述

  • 特点:

    固定大小的线程池,包含corePoolSize个核心线程。
    使用DelayedWorkQueue作为阻塞队列,支持延迟执行和定时周期性执行。

  • 使用场景:

    适用于需要按计划执行任务、延迟执行或周期性执行任务的情况,例如定时任务和调度任务


http://www.ppmy.cn/news/1148530.html

相关文章

文件上传 [极客大挑战 2019]Upload 1

题目来源&#xff1a;buuctf [极客大挑战 2019]Upload 1 打开题目 是一道文件上传题 我们上传png和jpg格式的一句话木马上去看看 上传失败了&#xff0c;应该是在后端也对我们的文件内容进行了检测&#xff0c;检测到我们的一句话木马里面包含<? 那我们重新换个一句话木…

c++ 学习之 强制类型转换运算符 const_cast

看例子怎么用 int main() {int a 1;int* p a;// 会发生报错// 如果学着 c的风格类型转换int* pp (int*)a;*pp 1; // 编译不报错&#xff0c;但是运行报错// const_castconst int n 5;const std::string s "lalal";// const cast 只针对指针&#xff0c;引用&…

MS4344:24bit、192kHz 双通道数模转换电路

MS4344 是一款立体声数模转换芯片&#xff0c;内含插值滤波器、 multi-bit 数模转换器、输出模拟滤波器。 MS4344 支持大部分 的音频数据格式。 MS4344 基于一个带线性模拟低通滤波器的 四阶 multi-bit Δ-Σ 调制器&#xff0c;而且本芯片可以通过检测信号频率 和主时钟频…

panads操作excel

panads简介 pandas是基于Numpy创建的Python包&#xff0c;内置了大量标准函数&#xff0c;能够高效地解决数据分析数据处理和分析任务&#xff0c;pandas支持多种文件的操作&#xff0c;比如Excel&#xff0c;csv&#xff0c;json&#xff0c;txt 文件等&#xff0c;读取文件之…

多线程并发篇---第五篇

系列文章目录 文章目录 系列文章目录一、什么是线程安全二、Thread类中的yield方法有什么作用?三、Java线程池中submit() 和 execute()方法有什么区别?一、什么是线程安全 线程安全就是说多线程访问同一段代码,不会产生不确定的结果。 又是一个理论的问题,各式各样的答案有…

第二章:OSI参考模型与TCP/IP模型

OSI参考模型与TCP/IP模型 一、OSI参考模型二、TCP/IP模型2.1 四层分法&#xff08;书上&#xff09;2.2 五层分法&#xff08;实际厂商&#xff09;2.3 数据封装和解封装2.3.1 封装2.3.2 解封装2.3.3 TCP/IP分层封装2.3.4 数据封装和解封装过程 一、OSI参考模型 1.物理层 定义电…

【Vuex+ElementUI】Vuex中取值存值以及异步加载的使用

一、导言 1、引言 Vuex是一个用于Vue.js应用程序的状态管理模式和库。它建立在Vue.js的响应式系统之上&#xff0c;提供了一种集中管理应用程序状态的方式。使用Vuex&#xff0c;您可以将应用程序的状态存储在一个单一的位置&#xff08;即“存储”&#xff09;中&#xff0c;…

搞流式计算,大厂也没有什么神话

抖音、今日头条&#xff0c;是字节跳动旗下最受用户欢迎的两款产品&#xff0c;也是字节跳动的门面。而在这背后&#xff0c;是众多技术团队在支撑&#xff0c;流式计算就是其中一支。 不过&#xff0c;即使是在字节跳动&#xff0c;搞流式计算也没有神话。只有一群年轻人&…