当前位置:   article > 正文

花几分钟看懂如何配置线程池的核心线程数_如何确定核心线程数

如何确定核心线程数

目录

一、背景

二、什么是CPU核数和逻辑处理器数

三、CPU密集型和IO密集型

四、线程池核心线程数确定

五、代码实现-ThreadPoolTaskExecutor


一、背景

为了实现异步,需要将任务开新的线程去处理调三方接口等,从而不影响主线程的工作。而配置线程池可以方便线程的管理,减少线程创建、摧毁带来的性能消耗和提高响应速度等。这篇文章将简单介绍如何配置线程池。

 

二、什么是CPU核数和逻辑处理器数

ctrl+alt+delete打开任务管理器,打开性能,如下图我的机器配置,可以发现右下角有内核和逻辑处理器

内核指的就是CPU核数逻辑处理器是线程数-最大可以并行的线程数(这里我搜索了无数的资料,验证了CPU核数和上述的内核是不同的描述而已,它两相等。如果还错,那真打脸了..)

有了这个知识,下面还要简单介绍一下CPU密集型和IO密集型

 

三、CPU密集型和IO密集型

线程任务可以分为CPU密集型和IO密集型。(平时开发基本上都是IO密集型任务)

CPU密集型任务的特点是进行大量的计算,消耗CPU资源,比如计算圆周率、视频高清解码等。这种任务操作都是比较耗时间的操作,任务越多花在任务切换的时间就越多,CPU执行任务效率就越低。所以,应当减少线程的数量,CPU密集型任务同时进行的数量应当等于CPU的核心数,上述我的电脑为8。

IO密集型的任务的特点是涉及到网络(调用三方接口)、磁盘IO(文件操作)等。这类任务操作是CPU消耗很少,任务大部分时间都在等待IO操作完成(IO的速度远低于CPU和内存的速度)。对于这种任务,任务越多,CPU效率越高,但是也有限度。我们开发接口时,像调别的应用接口,基本逻辑处理等,基本上都是属于IO密集型任务。

刚才提到了,CPU密集型尽量配置少的线程,核心线程配置:CPU核数。而IO线程池应配置多的线程,核心线程配置:CPU核数*2。这里IO密集型还有一种情况是线程易阻塞型的,需要计算阻塞系数,他是这么配置线程核心线程数的:CPU核数 / 1 – 阻塞系数(0.8~0.9之间)。

 

四、线程池核心线程数确定

Java给我们提供了获取逻辑处理器-也就是最大并行可以执行的线程数的API,如下图我的机器为16

核心线程计算:根据第三节的知识,及我的第一节应用背景是异步调三方接口,属于IO密集型,核心线程配置:CPU核数*2=8*2=16。(这里不考虑另一种情况配置)

其实API直接计算得到的值就是16,刚好满足,下面是实现代码,用的是ThreadPoolTaskExecutor,底层封装了ThreadPoolExecutor。后面专门写篇文章介绍一下。

 

五、代码实现-ThreadPoolTaskExecutor

其他参数不了解的可以参考我之前的文章介绍:超详细的线程池介绍

ThreadPoolTaskExecutor类不了解的或者它与ThreadPoolExecutor的区别参考另一篇文章:ThreadPoolTaskExecutor的点滴理解

线程池配置代码:(这里是通过Bean的方式)

  1. @Component
  2. public class ThreadPoolConfig {
  3.     /**
  4.      * 线程池名称
  5.      */
  6.     private static final String TASK_THREADPOOL_NAME = "task_threadpool_test";
  7.     /**
  8.      * 线程池配置
  9.      * @return 线程池
  10.      */
  11.     @Bean(TASK_THREADPOOL_NAME)
  12.     public Executor asyncThreadPoolExecutor() {
  13.         // 获得运行机器 逻辑处理器核数(其实就是线程数)
  14.         Integer availibleNum = Runtime.getRuntime().availableProcessors();
  15.         // 内部还是使用了ThreadPoolExecutor,只是参数配置更加方便
  16.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  17.         // 核心参数
  18.         executor.setCorePoolSize(availibleNum);
  19.         // 配置最大线程数 线程数*2
  20.         executor.setMaxPoolSize(availibleNum * 2);
  21.         // 配置队列容量 队列:不配置容量,用SynchronousQueue,配置用LinkedBlockingQueue。 (BlockingQueue)(queueCapacity > 0 ? new LinkedBlockingQueue(queueCapacity) : new SynchronousQueue());
  22.         executor.setQueueCapacity(100);
  23.         // 配置非核心线程超时释放时间(核心线程默认不允许回收)
  24.         executor.setKeepAliveSeconds(60);
  25.         // 配置线程名称前缀
  26.         executor.setThreadNamePrefix(TASK_THREADPOOL_NAME);
  27.         // 配置拒绝策略,CallerRunsPolicy:当拒绝时由调用线程处理该任务
  28.         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  29.         return executor;
  30.     }
  31. }

线程池使用代码:

  1. @Component
  2. public class MsgTaskUtils {
  3.     @Autowired
  4.     @Qualifier("task_threadpool_test")
  5.     private Executor asyncExecutor;
  6.     public void dealTask() {
  7.         // 模拟任务
  8.         Runnable runnable = new Runnable() {
  9.             @Override
  10.             public void run() {
  11.             }
  12.         };
  13.         // 通过线程池方式执行
  14.         asyncExecutor.execute(runnable);
  15.     }
  16. }

如果不用Bean的方式如何实现?代码如下(需要自己调用initalize初始化线程池)

  1. /**
  2.      * 自己new的,不会自己初始化,需要调用initialize才能用,否则报错
  3.      * java.lang.IllegalStateException: ThreadPoolTaskExecutor not initialized
  4.      */
  5.     public static ThreadPoolTaskExecutor asyncThreadPoolExecutor() {
  6.         // 获得运行机器 逻辑处理器核数(其实就是线程数)
  7.         Integer availibleNum = Runtime.getRuntime().availableProcessors();
  8.         // 内部还是使用了ThreadPoolExecutor,只是参数配置更加方便
  9.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  10.         // 核心参数
  11.         executor.setCorePoolSize(availibleNum);
  12.         // 配置最大线程数
  13.         executor.setMaxPoolSize(availibleNum * 2);
  14.         // 配置队列容量 队列:不配置容量,用SynchronousQueue,配置用LinkedBlockingQueue。 (BlockingQueue)(queueCapacity > 0 ? new LinkedBlockingQueue(queueCapacity) : new SynchronousQueue());
  15.         executor.setQueueCapacity(100);
  16.         // 配置非核心线程超时释放时间(核心线程默认不允许回收)
  17.         executor.setKeepAliveSeconds(60);
  18.         // 配置线程名称前缀
  19.         executor.setThreadNamePrefix(TASK_THREADPOOL_NAME);
  20.         // 配置拒绝策略,CallerRunsPolicy:当拒绝时由调用线程处理该任务
  21.         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  22.         // 不用bean注入方法,需要自己初始化线程池
  23.         executor.initialize();
  24.         return executor;
  25.     }


注意点:如果不是通过Bean的方式实现,那么需要手动去调用initialize方法去初始化线程池,因为ThreadPoolTaskExecutor-Bean实现了InitializingBean接口的Bean会调用,其里面的afterPropertiesSet去初始化线程池。


线程池核心线程数配置到此已经写完,有啥问题可以留言探讨。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/1004160
推荐阅读
相关标签
  

闽ICP备14008679号