【线程池】Java线程池的核心参数

news2025/1/10 11:42:29

目录

一、简介

二、构造方法

三、线程池的核心参数

3.1 corePoolSize 线程池核心线程大小

3.2 maximumPoolSize 线程池最大线程数量

3.3 keepAliveTime 空闲线程存活时间

3.4 unit 空间线程存活时间单位

3.5 workQueue 工作队列

①ArrayBlockingQueue

②LinkedBlockingQuene

③SynchronousQuene

④PriorityBlockingQueue

3.6 threadFactory 线程工厂

3.7 handler 拒绝策略

①CallerRunsPolicy

②AbortPolicy

③DiscardPolicy

④DiscardOldestPolicy


一、简介

ThreadPoolExecutor的构造方法是创建线程池的入口,虽然比较简单,但是信息量很大,由此也能引发一系列的问题,构造方法中有很多核心参数,这也是面试中经常被问到的问题。

Executors创建线程池也是基于ThreadPoolExecutor的构造方法实现的。定时任务线程池还基于ScheduledThreadPoolExecutor的构造方法,但归根结底还是基于ThreadPoolExecutor,所以下面我们对线程池核心参数的讲解都是基于ThreadPoolExecutor来分析的。

二、构造方法

ThreadPoolExecutor有四个构造方法,其中前三个最终都是调用最后一个,它有7个参数,分别为corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。

// 构造方法有不同的参数版本,以满足不同的需求
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    // 使用默认线程工厂和默认拒绝策略  Executors.defaultThreadFactory()和defaultHandler
    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);
}


/**
 * 用给定的初始参数创建一个新的ThreadPoolExecutor。
 * 所有的构造方法最终都会调用这个构造方法,都是基于这个方法实现的
 */
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;
}

ThreadPoolExecutor 3 个最重要的参数:

  • corePoolSize : 任务队列未达到队列容量时,最大可以同时运行的线程数量。
  • maximumPoolSize : 任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
  • workQueue:新任务来的时候会先判断当前运行(正在执行任务的线程)的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。

ThreadPoolExecutor其他常见参数 :

  • keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
  • unit: keepAliveTime 参数的时间单位。
  • threadFactory:executor(线程池)创建新线程的时候会用到。
  • handler:饱和策略。关于饱和策略下面单独介绍一下。

下面这张图可以加深你对线程池中各个参数的相互关系的理解(图片来源:《Java 性能调优实战》):

三、线程池的核心参数

参数说明

参数名

说明

corePoolSize

线程池维护线程的最少数量。线程池至少会保持改数量的线程存在,即使没有任务可以处理。(注意:这里说的至少是指线程达到这个数量后,即使有空闲的线程也不会释放,而不是说线程池创建好之后就会初始化这么多线程)

maximumPoolSize

线程池维护线程的最大数量。线程池最多可创建的线程数,即使队列中的任务满了线程数量也不会超过maximumPoolSize

keepAliveTime

线程池维护线程所允许的空闲时间。当线程池中的线程数量大于 corePoolSize时,超过corePoolSize的线程如果空闲时间超过keepAliveTime,线程将被终止

unit

线程池维护线程所允许的空闲时间的单位,和keepAliveTime配合使用

workQueue

线程池所使用的缓冲队列。ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue,PriorityBlockingQueue

handler

线程池对拒绝任务的处理策略。AbortPolicy,CallerRunsPolicy,DiscardOldestPolicy,DiscardPolicy,自定义

3.1 corePoolSize 线程池核心线程大小

线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,它们也不会被销毁,除非设置了allowCoreThreadTimeOut为true。所以默认情况下核心线程数量只会增多,不会减少,因为核心线程是不会被销毁的,除非显式的将线程销毁。

  • 当正在运行的线程数小于核心线程数时,来一个任务就创建一个核心线程;
  • 当正在运行的线程数大于或等于核心线程数时,任务来了先不创建线程而是丢到任务队列中。

3.2 maximumPoolSize 线程池最大线程数量

当正在运行的线程大于等于核心线程数的时,一个任务被提交到线程池后会被加入到任务队列中,在队列中等待核心线程将其取出并执行处理。但是如果任务提交的速度大于核心线程处理任务的速度,有界的任务队列就会被填满,当任务队列满了时,再来一个任务才会创建一个非核心线程,但线程池中核心线程数量 + 非核心线程数量 不能超过最大线程数。这个最大线程数量即由maximunPoolSize来指定。如果是无界的任务队列这个参数就没用了

3.3 keepAliveTime 空闲线程存活时间

一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。内部主要是通过阻塞队列带超时的poll(timeout, unit)方法实现的。

默认情况下,此参数仅当正在运行的线程数大于核心线程数时才有效,即只针对非核心线程。

但是,如果allowCoreThreadTimeOut被设置成了true,针对核心线程也有效。

3.4 unit 空间线程存活时间单位

keepAliveTime的计量单位。可选的单位有Days、HOURS、MINUTES、MILLISECONDS、MICROSECONDS、NANOSECONDS。

3.5 workQueue 工作队列

当正在运行的线程数大于或等于核心线程数时,任务来了是先进入任务队列中的。

这个队列必须是阻塞队列,所以像ConcurrentLinkedQueue这种就不能作为参数,因为它虽然是并发安全的队列,但是它不是阻塞队列,因为它并没有实现BlockingQueue接口。

当任务进入到工作队列后,在任务调度时再从队列中取出任务。线程池中比较常用的四种工作队列(阻塞队列):

ArrayBlockingQueue

基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。

LinkedBlockingQuene

基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的

SynchronousQuene

一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务(不会将该任务暂时寄存在队列中等待),如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略

SynchronousQuene同步队列没有任何内部容量,甚至连一个队列的容量都没有。

感觉ArrayBlockingQueue就像一个隧道,而SynchronousQueue就像一个门框(门框上不可以站人),进出队列就是穿门而过。

 

Executors提供的newCachedThreadPool线程池就是用了SynchronousQueue做任务队列,而它的核心线程数为0,最大线程数为无限大。

public static ExecutorService newCachedThreadPool() {
	return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
	                              60L, TimeUnit.SECONDS,
	                              new SynchronousQueue<Runnable>());
}

因为核心线程数为0,所以任务来时,只能新建线程池(如果没有空闲的线程),因为SynchronousQueue队列没有容量,不能存放任务,有了工作线程之后通过SynchronousQueue队列获取任务(让任务穿门而过,前一个任务不被线程领取,后边的任务不能过门)

所以这个线程池不能用于执行耗时的任务,因为他的最大线程数为无限大,很可能会建很多的线程。

 

PriorityBlockingQueue

具有优先级的无界阻塞队列,优先级通过参数Comparator实现。比较器(Comparable、Comparator)

3.6 threadFactory 线程工厂

创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程(守护线程)、查看创建线程数、给线程设置是否是后台运行、设置线程优先级等等。

默认使用的是Executors工具类中的DefaultThreadFactory类,这个类有个缺点,创建的线程的名称是自动生成的,无法自定义以区分不同的线程池,且它们都是非守护线程。

源码:

static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

那怎么自定义一个线程工厂呢?

其实也很简单,自己实现一个ThreadFactory,然后把名称和是否是守护进程当作构造方法的参数传进来就可以了。

3.7 handler 拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢?这里的拒绝策略,就是解决这个问题的。

拒绝策略表示当任务队列满了且线程数也达到最大了,这时候再新加任务,线程池已经无法承受了,这些新来的任务应该按什么逻辑来处理。

常用的拒绝策略有丢弃当前任务、丢弃最老的任务、抛出异常、调用者自己处理等待。

默认的拒绝策略是抛出异常(AbortPolicy),即线程池无法承载了,调用者再往里面添加任务会抛出异常。

默认的拒绝策略虽然比较简单粗暴,但是相对于丢弃任务策略明显要好很多,最起码调用者自己可以捕获这个异常再进行二次处理。

jdk中提供了四种拒绝策略(policy:政策,策略):

CallerRunsPolicy

该策略下,在调用者线程中直接执行被拒绝任务的run方法。如果线程池已经shutdown,则直接抛弃任务。

AbortPolicy

该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。(abort:放弃,丢弃)

DiscardPolicy

该策略下,直接丢弃任务,什么都不做。(discard:丢弃)

DiscardOldestPolicy

该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

到此,构造线程池的七个参数,就全部介绍完毕了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/580817.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式Linux中pinctrl 子系统和 gpio 子系统分析

目录 1、gpio 子系统 API 2、pinctrl 子系统 API 本文讲解 pinctrl 子系统和 gpio 子系统的 API&#xff0c;以及使用示例。 传统的配置 pin 的方式就是直接操作相应的寄存器&#xff0c;但是这种配置方式比较繁琐、而且容易出问题(比如 pin 功能冲突)。pinctrl 子系统就是为…

Linux :: 【基础指令篇 :: 文件及目录操作:(2)】::Linux操作系统的文件“框架”、绝对路径与相对路径及路径定位文件对象的解释

前言&#xff1a;本篇是 Linux 基本操作篇章的内容&#xff01; 笔者使用的环境是基于腾讯云服务器&#xff1a;CentOS 7.6 64bit。 学习集&#xff1a; C 入门到入土&#xff01;&#xff01;&#xff01;学习合集Linux 从命令到网络再到内核&#xff01;学习合集 本篇内容&am…

Linux常见IO模型

这篇博客开始我们Linux的最后一个章节--常见IO模型&#xff0c;在之前的博客当中我们讲述过Linux中基础的IO操作&#xff0c;欢迎大家去阅读。 我们通常指的IO操作便是数据的输入和输出&#xff0c;对应的具体操作过程我们可以将其分为两个步骤&#xff1a;等待IO就绪和数据拷…

Eclipse教程 Ⅵ

今天分享Eclipse Java 构建路径、Eclipse 运行配置(Run Configuration)和Eclipse 运行程序 Eclipse Java 构建路径 设置 Java 构建路径 Java构建路径用于在编译Java项目时找到依赖的类&#xff0c;包括以下几项&#xff1a; 源码包项目相关的 jar 包及类文件项目引用的的类…

postgresql 内核源码分析 语法解析 gram.y

专栏内容&#xff1a;postgresql内核源码分析个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e; 目录 前言 概述 流程简介 调用流程 语法解析详细分解 raw_parser的主流程 词法…

Wireshark使用手册

目录 前言 Wireshark不能做的 Wireshark VS Fiddler 同类的其他工具 什么人会用到wireshark wireshark 开始抓包 Wireshark 窗口介绍 Wireshark 显示过滤 保存过滤 过滤表达式的规则 封包列表(Packet List Pane) 封包详细信息 (Packet Details Pane) wireshark与…

【CVPR_2023论文精读】E4S: Fine-grained Face Swapping via Regional GAN Inversion

【CVPR_2023论文精读】E4S: Fine-grained Face Swapping via Regional GAN Inversion 0、前言Abstract1. Introduction2. Related Work2.1 GAN Inversion2.2 Face Swapping 3. Methodology3.1. Editing-for-Swapping (E4S) Framework3.1.1 Reenactment.3.1.2 Swapping and Gene…

学习:双重差分模型DIDPSM-基于Stata实现

双重差分模型 定义 双重差分法&#xff08;Difference in Differences&#xff09;: 通过利用观察学习的数据&#xff0c;计算自然实验中“实验组”与“对照组”在干预下增量的差距。 步骤&#xff1a; 分组&#xff1a;对于一个自然实验&#xff0c;其将全部的样本数据分为…

JMeter性能测试101:一步一步教你如何开始

1. Jmeter简介 Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。相比Loadrunner而言&#xff0c;JMeter小巧轻便且免费&#xff0c;逐渐成为了主流的性能测试工具&#xff0c;是每个测试人员都必须要掌握的工具之一。 2023年最新版Jmeter性能测试项目实战…

FFmpeg5.0源码阅读——mov文件格式解析

摘要&#xff1a;之前在Mp4格式详解中详细描述了Mp4文件格式的具体布局方式。为了更加深入理解mp4文件格式&#xff0c;本文记录了ffmpeg中解封装mp4文件的基本实现。关键字:mov、FFmpeg、mp4 1 简介 mp4文件格式是现如今网络上最常见的视频文件格式&#xff0c;其和mov等格式…

复合查询.

基本查询 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为大写的J select * from EMP where (sal>500 or jobMANAGER) and ename like J%;按照部门号升序而雇员的工资降序排序 select * from EMP order by deptno, sal desc;使用年薪进…

为建筑物的供暖系统实施MPC控制器的小型项目(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【网络】- TCP/IP四层(五层)协议 - 网际层(网络层) - 路由控制

目录 一、概述二、路由表(Routing table)三、最长匹配、默认路由、特定主机路由四 、IP数据报路由过程五、路由聚合 一、概述 网际协议 IP 大致分为三大作用模块&#xff0c; ①IP寻址、 ②路由&#xff08;最终节点为止的转发&#xff09; 、③IP分包与组包。前面两篇文章讨论…

dpdk ip分片报文重组处理

dpdk ip报文重组及分片API及处理逻辑介绍 DPDK的分片和重组实现零拷贝&#xff0c;详细介绍可以参阅DPDK分片与重组用户手则 相关数据结构 /** Fragmented packet to reassemble.* First two entries in the frags[] array are for the last and first fragments.*/ struct …

【测试平台开发】

【测试平台开发】 一、 后端开发 1、常见的技术架构与组件 语言&#xff1a; 项目注重高并发&#xff1a;选用go 注重区块链&#xff1a;选用go、rust(主打高性能) 大型浏览网站&#xff08;如电商&#xff09;&#xff1a;Java 技术架构与组件&#xff1a; 前端技术架构&a…

多元回归预测 | Matlab白鲸算法(BWO)优化BP神经网络回归预测,BWO-BP回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab白鲸算法(BWO)优化BP神经网络回归预测,BWO-BP回归预测,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %--------------…

供收藏:国内各种免费可用ChatGPT实测(兼验伪) 版本不断更新补充 更新日期:2023/05/28

文章目录 供收藏&#xff1a;国内各种免费可用ChatGPT实测(兼验伪) 版本不断更新补充 更新日期&#xff1a;2023/05/28国内大厂的人工智能语言模型国内可访问的ChatGPT资源&#xff08;创业公司&#xff09;ZelinAI&#xff08;国内可直接访问的ChatGPT&#xff09;注册邀请码网…

2023全国大学生信息安全竞赛(ciscn)初赛题解

战队信息 安全知识 甚至不用看视频&#xff0c;百度就有答案。除了那个最新的美国时政&#xff0c;其它的ChatGPT就能回答。 Misc 签到卡 关注公众号&#xff0c;根据提示&#xff0c;直接print(open(‘/flag’).read())&#xff1a; 国粹 脑洞题&#xff0c;给的题目原图…

【LeetCode热题100】打卡第6天:正则表达式匹配

文章目录 正则表达式匹配⛅前言&#x1f512;题目&#x1f511;题解 正则表达式匹配 ⛅前言 大家好&#xff0c;我是知识汲取者&#xff0c;欢迎来到我的LeetCode热题100刷题专栏&#xff01; 精选 100 道力扣&#xff08;LeetCode&#xff09;上最热门的题目&#xff0c;适合…

从原理总结chatGPT的Prompt的方法

一 什么是chatGPT chatGPT全称是Generative Pre-trained Transformer&#xff0c;它是一种专注于对话生成的语言模型&#xff0c;可以根据用户的文本输入&#xff0c;做出相应的智能回答。chatGPT是由OpenAI于2018年研发的语言模型&#xff0c;其中OpenAI是于2015年由特斯拉的…