Java并发编程—线程池参数配置、线程池配置、线程池监控

一、线程池的参数配置

在这里插入图片描述

1.corePoolSize核心线程数选择

配置线程数量之前,首先要看任务的类型是 IO密集型,还是CPU密集型?
什么是IO密集型?比如:频繁读取磁盘上的数据,或者需要通过网络远程调用接口。
什么是CPU密集型?比如:非常复杂的调用,循环次数很多,或者递归调用层次很深等。
IO密集型配置线程数经验值是:2N,其中N代表CPU核数。
CPU密集型配置线程数经验值是:N + 1,其中N代表CPU核数。

如何获取CPU核数?

int availableProcessors = Runtime.getRuntime().availableProcessors();

2.workQueue工作队列(阻塞队列)选择

线程池中常用的阻塞队列有三种

BlockingQueue<Runnable> workQueue = null;
workQueue = new SynchronousQueue<>();//无缓冲的等待队列
workQueue = new ArrayBlockingQueue<>(5);//基于数组的先进先出队列
workQueue = new LinkedBlockingQueue<>();//基于链表的先进先出队列

如何选择?看看三者的区别

SynchronousQueue是一个不存储元素的阻塞队列,适合传递性场景,只是负责把父线程提交的任务直接交给线程池线程处理。也就是说提交的任务数超过最大线程数就会执行拒绝策略

ArrayBlockingQueue底层是用数组实现的有界阻塞队列,因为需要传初始值(如果传Integer最大值,也类似于无界了)。队列按照先进先出的原则对元素进行排序

LinkedBlockingQueue底层是用链表实现的有界阻塞队列,如果不传初始化值为Integer最大值,也是先进先出对元素进行排序

一般选择建议选择有界队列,因为如果任务特别多,核心线程处理不过来,会将任务都放到工作队列中,此时最大线程数已经没有意义了。如果控制不好会导致OOM

那么ArrayBlockingQueue和LikendBlockingQueue选择哪一个?

从底层实现来看LikendBlockingQueue需要维护一个个Node对象,需要额外的内存消耗。并且在生产和消费的时候,需要创建Node对象进行插入或移除,大批量数据的系统中,其对于GC的压力会比较大。

ArrayBlockingQueue只是维护final Object[] items;一个数组。在生产和消费的时候,是按照索引对数据插入或移除的,不会产生或销毁任何额外的对象实例

综合来说,可以选择ArrayBlockingQueue。

3.阻塞队列长度和最大线程数选择

这个问题网上大多回答是根据项目配置、响应时间要求来判断,或者根据公式计算。我觉得还是要根据具体项目来选择,比如可能要求在一定的响应时间内完成需求。

拿具体项目举例吧
项目需求

1.从项目中将客户数据导出pdf,然后压缩,下载下来
2.使用线程池子线程导出每个客户的pdf,压缩使用主线程
3.每个客户导出pdf的时间大概在1.5s左右,一组客户平均在60人,最大在150人
4.响应时间要在3s内完成

那么阻塞队列长度和最大线程数应该怎么设置?
因为响应时间在3s内,每一个pdf生成需要1.5s,那么可以将一组客户分为两组执行,一半放到阻塞队列,一半直接创建非核心线程执行。按组数人最大的进行分组,那么最大线程数和阻塞队列数对半分。还需要考虑主线程执行时间,那么可以设置非核心线程数大一点。推导的最后结果就是最大线程数可以设置成100,阻塞队列长度也可以设置成100。记得一定要回收非核心线程,配置keepAlivedTime。

4.拒绝策略选择

JDK提供了四种拒绝策略

  • AbortPolicy:直接丢弃新任务,抛出异常
  • DiscardPolicy:直接丢弃掉,不会抛出异常
  • DiscardOldestPolicy:丢弃时间最久的任务。一般是队列最前面的任务
  • CallerRunsPolicy:交给主线程去执行

当然也可以自定义拒绝策略,如果你的任务不能被拒绝的话,可以让任务重回队列,重新执行或者交给主线程执行。在下面例子中会写到。

二、SpringBoot环境下配置线程池

1.线程池配置

一般在Spring环境下,我们可以将ThreadPoolExecutor作为一个Bean交给Spring管理。配置如下

//1.yml文件配置
demo:
  thread:
    coreSize: 8
    maxSize: 100
    keepAliveTime: 60
    queueLength: 100

//2.读取yml文件配置
@ConfigurationProperties(prefix = "demo.thread")
@Data
public class ThreadPoolConfigProperties {

    private Integer coreSize;

    private Integer maxSize;

    private Integer keepAliveTime;

    private Integer queueLength;

}

//3.配置ThreadPoolExector的bean
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool){
        return new ThreadPoolExecutor(
                pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(pool.getQueueLength()),
                Executors.defaultThreadFactory(),
                new RejectedExecutionHandler() {
                    @SneakyThrows
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        while (executor.getQueue().offer(r,5,TimeUnit.SECONDS)){
                            break;
                        }
                    }
                }
        );
    }
}

//4.使用,直接测试即可
 @Autowired
 private ThreadPoolExecutor executor;

 @RequestMapping("/threadtest")
 public void test(){
     for (int i = 0;i<100;i++){
         executor.execute(()->{
             System.out.println("sdf");
         });
     }
 }

学习下两个注解
@ConfigurationProperties和@EnableConfigurationProperties。两者的关系简单点说就是@EnableConfigurationProperties让@ConfigurationProperties注解的类的生效,并且将@ConfigurationProperties注解的bean信息作为spring的环境bean,可以直接取出数据
在这里插入图片描述

2.线程池的监控

线程池让线程得以复用、得以管理,但是线程池一旦使用不当可能会造成服务宕机、内存溢出等问题,所以我们可以监控下线程池。线程池提供一些方法可以获取相关的信息
在这里插入图片描述
拿到这些信息有什么用呢?还记得之前的SpringBoot Actuator吗,可以通过该机制将线程池的运行状态暴露出去,通过prometheus采集然后展示就可以了。


参考文章:
常见的8种拒绝策略:https://zhuanlan.zhihu.com/p/142254564
线程池企业级应用:https://blog.csdn.net/AlbenXie/article/details/105292727

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿成长轨迹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值