java线程池参数自定义设置详解

 更新时间:2023年11月15日 10:11:54   作者:zhongh Jim  
这篇文章主要为大家介绍了java线程池参数自定义设置详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

上一篇线程池+ FutureTask异步执行多任务只介绍了怎么搭配使用线程池,但没有说明里面的线程池的参数是怎么设置的,那么本文就说明一下。

这里把上篇文章的线程池参数设置贴出来:

//给这个接口的线程池定义里边的线程名字
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-start-runner-%d").build();
ExecutorService taskExe= new ThreadPoolExecutor(10,20,800L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(100),namedThreadFactory);

这些参数也不都是随意设置的,而是有一定的考量思路,下面会一 一介绍

先介绍一下线程池的构造函数

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

我们创建线程池一般是手动设置线程池的参数,已经不建议使用Executors的FixedThreadPool 、SingleThreadPool、CachedThreadPool了

因为:

  • FixedThreadPool 、SingleThreadPool会的任务等待队列均为new LinkedBlockingQueue<Runnable>(),允许的队列长度为 Integer.MAX_VALUE,存在任务堆积导致OOM内存溢出的隐患
  • 而CachedThreadPool允许的最大线程数量为Integer.MAX_VALUE,而且核心线程数为0,意味着 只要有任务进来,就会频繁创建新线程,没有任务之后又要关闭线程,耗费性能。另一方面,由于允许创建大量的线程,也有导致OOM的潜在隐患

设置线程池参数需要参考几个数值:

tasks:每秒任务数,运维反馈是平均每秒 38个

taskcost:每个任务花费时间,0.2s

(3) responsetime:系统容忍(线程等待最长时间)的最大时间1s

corePoolSize:核心线程数

核心线程会一直存活,不管空不空闲,但如果设置了setAllowCoreThreadTimeout(true)会让核心线程在空闲超时后关闭

计算方式:corePoolSize=tasks/(1/taskcost) =tasks * taskcost =38*0.2=7.6 个

查阅了下文章,大佬说计算密集型(遍历+判断的逻辑耗时占比多)的接口可将核心线程设置为:

corePoolSize=CPU核数+1 =8+1=9,设置为10就好了

如何查看CPU核数:

System.out.println(Runtime.getRuntime().availableProcessors());

设置得稍微大一点,也能减少频繁创建额外线程带来的开销

maxPoolSize:最大线程数

如果核心线程数不够用,会创建额外的线程来执行任务。

创建额外线程的条件(缺一不可):

  • 现有的线程数< 最大线程数maxPoolSize and 现有线程数 > corePoolSize核心线程数
  • 任务队列填满了

最大线程数我们设置的相对随意了些, 令maxPoolSize= 2* corePoolSize=20,大概能应对突然暴增的业务查询请求

keepAliveTime额外线程的可空闲时间

额外线程就是在核心线程数的基础上 另外创建的线程

额外线程空闲了keepAliveTime的时间后,线程退出,直至现有的线程数量=corePoolSize核心线程数

  • TimeUnit.MILLISECONDS是毫秒单位

workQueue任务队列

常见的有3种:

(1) 无限队列LinkedBlockingQueue()

构造函数是new LinkedBlockingQueue<Runnable>()

允许的任务等待队列的最大长度为:Integer.MAX_VALUE,即能无限的接收新的任务,任何的拒绝策略也差不多没有意义了

另外,maximumPoolSize这个参数也没有意义了,因为只有同时满足 核心线程数量够了 + 任务队列workQueue满了 + 现有的线程数<maximumPoolSize最大线程数,才会去创建额外的线程

  • 好处是LinkedBlockingQueue在应对突然暴增的请求时,它不会抛异常拒绝
  • 缺点是任务堆积过度没有及时处理的话,容易导致内存溢出

那咱们就不用这个队列了吧

(2) 有界队列

  • new LinkedBlockingQueue(int capacity):固定容量的阻塞队列
  • new ArrayBlockingQueue<Integer>(int capacity,true);其中true是公平锁,只能FIFO排队一 一执行;false允许任务插队,会存在晚来的任务先执行的情况
  • PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator):默认会创建长度为11的优先级队列,第二个参数comparator会按照我们指定的方式进行排序

我们的任务基本的执行顺序基本也是先进先出,直接用了new LinkedBlockingQueue(int capacity),把容量设置得大一点,那样就不会轻易的填满队列导致频繁地创建额外的线程,减少线程频繁切换

(3) SynchronousQueue

new SynchronousQueue():队列长度为0,要添加新任务必须得有空闲的线程才能添加,因此要求 maximumPoolSize尽可能的大,还得 配置拒绝策略

最终, 我们选择了new LinkedBlockingQueue(int capacity)作为任务队列

任务队列的长度

queueCapacity = (coreSize/taskcost) * responsetime=8/0.2*1=80,队列长度设置为100也可

RejectedExecutionHandler拒绝策略

  • AbortPolicy:抛异常
  • DiscardPolicy:丢任务
  • DiscardOldestPolicy:将队列头部的任务丢了,也就是把最早进入队列等待的任务丢了
  • CallerRunsPolicy:将新任务(皮球)踢回给主线程执行,让主线程在接下来的时间能无法提交新任务,典型的踢皮球策略

我们选择了默认的AbortPolicy抛异常:

抛异常的话,需要上游系统截获异常,并告知用户请求繁忙稍等一下

如果是DiscardPolicy丢任务的话我猜大概率是用户得不到响应吧,没这么搞过

线程工厂

它还是很有必要设置的,因为系统的线程池不止一个,不设置一下线程工厂,不给线程定义个名字的话,很难看到是哪个线程池的线程在跑,因为线程的名字都被写死成pool-1-thread-1pool-1-thread-2pool-2-thread-1

那么,问题来了:如何判断线程池里边的指定线程是否在执行任务?

更多关于线程池参数自定义的资料请关注脚本之家其它相关文章!

相关文章

  • Mybatis中自定义TypeHandler处理枚举的示例代码

    Mybatis中自定义TypeHandler处理枚举的示例代码

    typeHandler,是 MyBatis 中的一个接口,用于处理数据库中的特定数据类型,下面简单介绍创建自定义 typeHandler 来处理枚举类型的示例,感兴趣的朋友跟随小编一起看看吧
    2024-01-01
  • Java 输入输出 IO NIO AIO三兄弟对比分析对比分析

    Java 输入输出 IO NIO AIO三兄弟对比分析对比分析

    这篇文章主要为大家介绍了Java 输入输出 IO NIO AIO三兄弟对比分析对比分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • springboot整合jcasbin权限管理

    springboot整合jcasbin权限管理

    jcasbin的权限控制非常简单,本文就来介绍一下springboot整合jcasbin权限管理,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • DolphinScheduler容错源码分析之Worker

    DolphinScheduler容错源码分析之Worker

    这篇文章主要为大家介绍了DolphinScheduler容错源码分析之Worker,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • 深入理解Java中包的定义与使用

    深入理解Java中包的定义与使用

    在开发过程中,会定义很多类,为了避免相同类名称出现而发生覆盖的情况,把所有java程序保存在各自的目录里面,而该目录就是包。包的本质实际上就是一个文件夹。本文将给大家详细的介绍,对大家的学习或工作具有一定的参考借鉴价值
    2021-09-09
  • Java中Easyexcel 实现批量插入图片功能

    Java中Easyexcel 实现批量插入图片功能

    这篇文章主要介绍了Easyexcel 实现批量插入图片,本文通过实例代码给大家介绍了easyexcel文档处理工具、自定义图片处理器的相关知识,需要的朋友可以参考下
    2022-04-04
  • 使用nacos实现自定义文本配置的实时刷新

    使用nacos实现自定义文本配置的实时刷新

    我们都知道,使用Nacos时,如果将Bean使用@RefreshScope标注之后,这个Bean中的配置就会做到实时刷新,本文给大家介绍了如何使用nacos实现自定义文本配置的实时刷新,需要的朋友可以参考下
    2024-05-05
  • java图片缩放实现图片填充整个屏幕

    java图片缩放实现图片填充整个屏幕

    这篇文章主要介绍了java图片缩放实现图片填充整个屏幕,本文提供了两种解决方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-05-05
  • 使用自定义参数解析器同一个参数支持多种Content-Type

    使用自定义参数解析器同一个参数支持多种Content-Type

    这篇文章主要介绍了使用自定义参数解析器同一个参数支持多种Content-Type的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java将CSV的数据发送到kafka的示例

    Java将CSV的数据发送到kafka的示例

    这篇文章主要介绍了Java将CSV的数据发送到kafka得示例,帮助大家更好得理解和使用Java,感兴趣的朋友可以了解下
    2020-11-11

最新评论