当前位置: 首页 > news >正文

线程池七个参数的含义

Java中的线程池里七个参数的以及其各自的含义

面试题:说一下线程池七个参数的含义?

所谓的线程池的 7 大参数是指,在使用 ThreadPoolExecutor 创建线程池时所设置的 7 个参数,如以下源码所示:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {
}

这 7 个参数分别是:

  1. corePoolSize:核心线程数。
  2. maximumPoolSize:最大线程数。
  3. keepAliveTime:空闲线程存活时间。
  4. TimeUnit:时间单位。
  5. BlockingQueue:线程池任务队列。
  6. ThreadFactory:创建线程的工厂。
  7. RejectedExecutionHandler:拒绝策略。

参数1:corePoolSize

核心线程数:是指线程池中长期存活的线程数。

这就好比古代大户人家,会长期雇佣一些“长工”来给他们干活,这些人一般比较稳定,无论这一年的活多活少,这些人都不会被辞退,都是长期生活在大户人家的。

参数2:maximumPoolSize

最大线程数:线程池允许创建的最大线程数量,当线程池的任务队列满了之后,可以创建的最大线程数。

这是古代大户人家最多可以雇佣的人数,比如某个节日或大户人家有人过寿时,因为活太多,仅靠“长工”是完不成任务,这时就会再招聘一些“短工”一起来干活,这个最大线程数就是“长工”+“短工”的总人数,也就是招聘的人数不能超过 maximumPoolSize。

注意事项

最大线程数 maximumPoolSize 的值不能小于核心线程数 corePoolSize,否则在程序运行时会报 IllegalArgumentException 非法参数异常,如下图所示:
在这里插入图片描述

参数3:keepAliveTime

空闲线程存活时间,当线程池中没有任务时,会销毁一些线程,销毁的线程数=maximumPoolSize(最大线程数)-corePoolSize(核心线程数)。

还是以大户人家为例,当大户人家比较忙的时候就会雇佣一些“短工”来干活,但等干完活之后,不忙了,就会将这些“短工”辞退掉,而 keepAliveTime 就是用来描述没活之后,短工可以在大户人家待的(最长)时间。

参数4:TimeUnit

时间单位:空闲线程存活时间的描述单位,此参数是配合参数 3 使用的。参数 3 是一个 long 类型的值,比如参数 3 传递的是 1,那么这个 1 表示的是 1 天?还是 1 小时?还是 1 秒钟?是由参数 4 说了算的。TimeUnit 有以下 7 个值:

  • TimeUnit.DAYS:天
  • TimeUnit.HOURS:小时
  • TimeUnit.MINUTES:分
  • TimeUnit.SECONDS:秒
  • TimeUnit.MILLISECONDS:毫秒
  • TimeUnit.MICROSECONDS:微妙
  • TimeUnit.NANOSECONDS:纳秒

参数5:BlockingQueue

在Java中,BlockingQueue是一个接口,它的实现类有ArrayBlockingQueue、DelayQueue、 LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等,它们的区别主要体现在存储
结构上或对元素操作上的不同,但是对于take与put操作的原理,却是类似的,目的都是阻塞存取。

有界与无界

有界队列:就是有固定大小的队列。比如设定了固定大小的 LinkedBlockingQueue,又或者大小为 0,只是在生产者和消费者中做中转用的 SynchronousQueue。

无界队列:指的是没有设置固定大小的队列。这些队列的特点是可以直接入列,直到溢出。当然现实几乎不会有到这么大的容量(超过 Integer.MAX_VALUE),所以从使用者的体验上,就相当于 “无界”。比如没有设定固定大小的 LinkedBlockingQueue。

阻塞与非阻塞

阻塞和非阻塞指的是调用者(程序)在等待返回结果(或输入)时的状态。阻塞时,在调用结果返回前,当前线程会被挂起,并在得到结果之后返回。非阻塞时,如果不能立刻得到结果,则该调用者不会阻塞当前线程。因此对应非阻塞的情况,调用者需要定时轮询查看处理状态。

入队

add(E e):(非阻塞)调用offer但会根据offer结果,如果false抛出 IllegalStateException(“Queue full”)
offer(E e):(非阻塞)如果队列没满,立即返回true; 如果队列满了,立即返回false
put(E e):(阻塞)如果队列满了,一直阻塞,直到队列不满了或者线程被中断
offer(E e, long timeout, TimeUnit unit):在队尾插入一个元素,,如果队列已满,则进入等待,直到出现以下三种情况:
1.被唤醒
2.等待时间超时
3.当前线程被中断

出队

poll():(非阻塞)如果没有元素,直接返回null;如果有元素,出队
remove():(非阻塞)删除队列头元素,如果没有元素,返回false
take():(阻塞)如果队列空了,一直阻塞,直到队列不为空或者线程被中断
poll(long timeout, TimeUnit unit):如果队列不空,出队;如果队列已空且已经超时,返回null;如果队列已空且时间未超时,则进入等待,直到出现以下三种情况:
1.被唤醒
2.等待时间超时
3.当前线程被中断

查看元素

element(): 调用peek(),查看元素,拿到为null,抛出 NoSuchElementException。
peek():查看元素,不去除,如果拿不到则为null。

阻塞队列

阻塞队列:是一种特殊的队列,它在普通队列的基础上提供了两个附加功能。
在这里插入图片描述

即:

  1. 当队列为空的时候,获取队列中元素的消费者线程会被阻塞,同时唤醒生产者线程。
  2. 当队列满了的时候,向队列中添加元素的生产者线程被阻塞,同时唤醒消费者线程。

在线程池中,阻塞队列是用来存储线程池的所有待执行任务的队列。它可以设置以下几个值:

- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。特点:ArrayBlockingQueue底层是使用一个数组实现队列的,内部使用了一把锁对插入和取出做了限制,即插或者取的操作是原子性容量:需要指定一个大小,创建了无法修改元素:不允许为null的元素插入
- LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。特点:内部有两把锁,即入队锁和出队锁(ReentrantLock+Condition),插入和取出各一把,互不打扰。两把锁来控制插入和取出数组阻塞唤醒。内部通过AtomicInteger count变量保证统计队列元素准确容量:默认为Integer.MAX_VALUE元素:不允许为null的元素插入
- SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。一种无缓冲的等待队列,相对于有缓冲的BlockingQueue来说,少了一个中间经销商的环节(缓冲区)。消费者必须亲自去集市找到所要商品的直接生产者特点:一对一,生产者和消费者缺一就阻塞,存在公平和非公平两种容量:size默认为0,剩余容量也为0元素:不允许为null的元素插入
- PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。特点:无界队列依赖Comparator来确保不同元素的排序位置,最大值不超过Integer.MAX_Value-8容量:默认大小为11,底层使用数组来存储,会扩容元素:不允许为null的元素插入
- DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。特点:存储Delayed元素,可实现延时等功能容量:默认为11,底层使用PriorityBlockingQueue来存储元素:不允许为null的元素插入,内部存储Delay的实现类元素take:内部使用priorityblockingqueue排序,根据getDelay判断剩余时间,只有当前到点了,才可以取出元素
- LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。特点:BlockingDeque 类是一个双端队列,在不能够插入元素时,它将阻塞住试图插入元素的线程;在不能够抽取元素时,它将阻塞住试图抽取的线程。容量:可以指定队列的容量(防止过度膨胀),如果不指定,默认容量大小等于Integer.MAX_VALUE。元素:同时支持FIFO和FILO两种操作方式(即可以从队列的头和尾同时操作(插入/删除)),支持线程安全。
- LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。public interface TransferQueue<E> extends BlockingQueue<E> {// 如果可能,立即将元素转移给等待的消费者。 // 如果存在消费者已经等待接收它(在 take 或 timed poll(long,TimeUnit)poll)中,则立即传送指定的元素,否则返回 false。boolean tryTransfer(E e);// 将元素转移给消费者,如果需要的话等待。 // 如果存在一个消费者已经等待接收它(在 take 或timed poll(long,TimeUnit)poll)中,则立即传送指定的元素,否则等待直到元素由消费者接收。void transfer(E e) throws InterruptedException;// 上面方法的基础上设置超时时间boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException;// 如果至少有一位消费者在等待,则返回 trueboolean hasWaitingConsumer();// 返回等待消费者人数的估计值int getWaitingConsumerCount();}特点:实现了TransferQueue接口。TransferQueue接口继承了BlockingQueue,主要扩展了两个方法tryTransfer、transfer。对比:和SynchronousQueue.TransferQueue(公平模式)相比,它是可以统计长度,可以进行查询的;和LinkedBlockingQueue相比,它拥有更高的性能(使用CAS自旋);和ConcurrentLinkedQueue相比,它拥有阻塞功能。因此可以看作是ConcurrentLinkedQueue、SynchronousQueue、LinkedBlockingQueue的超集,作为对比学习。既然说到了,那就顺便说一下ConcurrentLinkedQueue吧。总结:ArrayBlockingQueue:需要创建队列数组长度。LinkedBlockingQueue:内部使用Node实现,默认大小Integer.MAX_VALUE。PriorityBlockingQueue:优先级队列,默认大小11,内部需实现Comparator来比较。DelayQueue:延时队列,元素需要实现Delayed,底层使用PriorityBlockingQueue,默认大小11。SynchronousQueue:交换队列,默认大小0,需同时存在生产者和消费者,否则任一都会阻塞LinkedTransferQueue:新增transfer方法,tryTransfer和transfer可以检测是否有线程在等待获取数据,如果检测到就立即发送新增的数据给这个线程获取而不用放入队列。
-----------------------------------------------------------------------------------------	
顺便提一下ConcurrentLinkedQueue。
- ConcurrentLinkedQueue:是一种非阻塞的无界的线程安全队列,与阻塞队列LinkedBlockingQueue相对应,ConcurrentLinkedQueue同样也是使用链表实现的FIFO队列,但不同的是它没有使用任何锁机制,而是用自旋+CAS来实现线程安全。特点:
.不允许null入列
.在入队的最后一个元素的next为null
.队列中所有未删除的节点的item都不能为null且都能从head节点遍历到
.删除节点是将item设置为null, 队列迭代时跳过item为null节点
.head节点跟tail不一定指向头节点或尾节点,可能存在滞后性 

比较常用的是 LinkedBlockingQueue,线程池的排队策略和 BlockingQueue 息息相关。

参数6:ThreadFactory

线程工厂:线程池创建线程时调用的工厂方法,通过此方法可以设置线程的优先级、线程命名规则以及线程类型(用户线程还是守护线程)等。线程工厂的使用示例如下:

public static void main(String[] args) {// 创建线程工厂ThreadFactory threadFactory = new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {// 创建线程池中的线程Thread thread = new Thread(r);// 设置线程名称thread.setName("Thread-" + r.hashCode());// 设置线程优先级(最大值:10)thread.setPriority(Thread.MAX_PRIORITY);//......return thread;}};// 创建线程池ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 10, 0,TimeUnit.SECONDS, new LinkedBlockingQueue<>(),threadFactory); // 使用自定义的线程工厂threadPoolExecutor.submit(new Runnable() {@Overridepublic void run() {Thread thread = Thread.currentThread();System.out.println(String.format("线程:%s,线程优先级:%d",thread.getName(), thread.getPriority()));}});
}

以上程序的执行结果如下:

在这里插入图片描述

从上述执行结果可以看出,自定义线程工厂起作用了,线程的名称和线程的优先级都是通过线程工厂设置的。

参数7:RejectedExecutionHandler

拒绝策略:当线程池的任务超出线程池队列可以存储的最大值之后,执行的策略。默认的拒绝策略有以下 4 种:

  • AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  • CallerRunsPolicy:使用当前调用的线程来执行此任务。
  • DiscardOldestPolicy:丢弃队列头部(最旧)的一个任务,并重新执行当前任务(重复此过程)。
  • DiscardPolicy:也是丢弃任务,但是不抛出异常。

线程池的默认策略是 AbortPolicy 拒绝并抛出异常。

案例分析

线程池中线程数小于corePoolSize时,新任务将创建一个新线程执行任务,不论此时线程池中是否存在空闲线程。

线程池中线程数达到corePoolSize时,新任务将被放入workQueue中,等待线程池中任务调度执行;

当workQueue已满,且maximumPoolSize>corePoolSize时,新任务会创建新线程执行任务;

当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理;

当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收该线程;

如果设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收;

总结

本文介绍了线程池的 7 大参数:

  1. corePoolSize:核心线程数,线程池正常情况下保持的线程数,大户人家“长工”的数量。
  2. maximumPoolSize:最大线程数,当线程池繁忙时最多可以拥有的线程数,大户人家“长工”+“短工”的总数量。
  3. keepAliveTime:空闲线程存活时间,没有活之后“短工”可以生存的最大时间。
  4. TimeUnit:时间单位,配合参数 3 一起使用,用于描述参数 3 的时间单位。
  5. BlockingQueue:线程池的任务队列,用于保存线程池待执行任务的容器。
  6. ThreadFactory:线程工厂,用于创建线程池中线程的工厂方法,通过它可以设置线程的命名规则、优先级和线程类型。
  7. RejectedExecutionHandler:拒绝策略,当任务量超过线程池可以保存的最大任务数时,执行的策略。

相关文章:

  • python有序列表
  • 【Triton 教程】triton_language.full
  • C++手撕STL-其一
  • 大语言模型的训练、微调及压缩技术
  • InternVL 3的技术深度分析,代码与原理
  • 【ELF2学习板】交叉编译FFTW
  • 记录学习的第二十九天
  • 浅析数据库面试问题
  • Langchain+chain+数据库操作
  • 守护者进程小练习
  • 2025年03月中国电子学会青少年软件编程(Python)等级考试试卷(二级)答案 + 解析
  • 线上健身预约小程序源码介绍
  • C++ 20 信号量详解
  • 梯度下降,共轭梯度,牛顿法,拟牛顿法的收敛速度对比
  • SAP 查找JOB进程ID
  • 05、GPIO外设(五):CubeMX配置
  • SQL:聚合函数(Aggregate Functions)
  • Java 接入deepseek(非流式)
  • 基于autoware.1.14与gazebo联合仿真进行激光点云循迹仿真
  • 基于领域知识的A型主动脉夹层综合分割及面向临床的评估|文献速递-深度学习医疗AI最新文献
  • 市场监管总局:在全国范围内开展广告市场秩序整治
  • 游客参加泼水节被喷伤左眼,西双版纳告庄景区:禁用高压水枪
  • 光大保德信基金总经理刘翔离任,年内已有59家公募变更高管
  • 公安机关依法严打危害生态和生物安全犯罪,公布一批典型案例
  • 从绿绒蒿到植物远征计划:植物学知识的生产与权力(上)