首页 > 编程语言 >Java配置线程池

Java配置线程池

时间:2023-05-24 15:36:26浏览次数:54  
标签:Java 配置 任务 线程 IO executor 执行 CPU

一、Java配置线程池

1、线程池分类、其他

1.1、分类

IO密集型 和 CPU密集型 任务的特点不同,因此针对不同类型的任务,选择不同类型的线程池可以获得更好的性能表现。

1.1. IO密集型任务

​ IO密集型任务的特点是需要频繁读写磁盘、网络或者其他IO资源,执行时间长,CPU占用率较低。

对于这类任务,线程的执行时间主要取决于IO操作的速度,而非CPU的执行能力。

​ 因此,线程池的线程数应该设置较大,以便充分利用IO资源。

通常建议使用CachedThreadPool线程池或者FixedThreadPool线程池来处理IO密集型任务。

1.2. CPU密集型任务

​ CPU密集型任务的特点是需要进行大量的计算,执行时间长,CPU占用率较高。

对于这类任务,线程的执行时间主要取决于CPU的执行能力。

​ 因此,线程池的线程数应该设置较小,以充分利用CPU的计算能力,避免过多的线程切换和上下文切换导致的性能损失。

通常建议使用FixedThreadPool线程池或者SingleThreadPool线程池来处理CPU密集型任务。


总之,选择恰当的线程池类型可以充分发挥不同类型任务的性能,提高程序效率和响应速度。

1.2. 异步线程池的选择

对于异步线程池,通常建议使用IO密集型线程池。
异步任务通常是网络IO或磁盘IO等操作,这些操作的执行时间相对于CPU计算的执行时间要长得多。
使用IO密集型线程池可以更好地利用IO资源,提高多个异步任务的执行效率和吞吐量,
同时避免由于过多的线程切换和上下文切换导致的性能损失。

1.3. 线程池工作步奏

很多任务——》线程池创建核心线程——》任务超过最大线程——》把任务放入队列中等待执行——》队列中放满了——》进入处理策略

2、线程池参数、合理参数

2.1、参数

①、核心线程数

当线程池中的线程数量为 corePoolSize核心线程数 时,即使这些线程处于空闲状态,也不会销毁(除非设置 allowCoreThreadTimeOut=true)。
//  -> 核心线程,也就是正在处理中的任务
//  -> 虽然 CPU 核心数可以作为线程池中线程数量的参考指标,但最终线程数量还需要根据具体情况进行设置和调整。
//  -> 如果同时运行的线程数量超过 CPU 核心数,就会发生--线程上下文切换--,导致额外的开销和性能下降。所以线程不能创建得过多

②、最大线程数

线程池中允许的线程数量的最大值。
        //  -> 当线程数 = maxPoolSize最大线程数时,还有新任务,就会放进队列中等待执行 ↓↓↓

③、队列长度

当核心线程数达到最大时,新任务会放在队列中排队等待执行
        //  -> 根据业务配置,如果队列长度过大,可能会导致系统内存资源占用过高,最终导致 OOM,需要注意控制
        //  -> 如果需要执行的任务装满了队列,就会走拒绝策略 ↓↓↓

④、拒绝策略

(官方提供4种,也可以自定义):因达到线程边界和任务队列满时,针对新任务的处理方法。
        // -> AbortPolicy:直接丢弃任务并抛出 RejectedExecutionException 异常。(默认策略)
        // -> DiscardPolicy:直接丢弃掉,不会抛出异常
        // -> DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
        // -> CallerRunsPolicy:交给主线程(调用线程)去执行

⑤、空闲线程存活时间

(默认60s):设置当前线程池中空闲线程的存活时间,即线程池中的线程如果有一段时间没有任务可执行,则会被回收掉。
        //  -> 当线程池中的线程数大于 corePoolSize 时,多余的空闲线程将在销毁之前等待新任务的最长时间。
        //  -> 如果一个线程在空闲时间超过了 keepAliveSeconds,且当前线程池中线程数量大于 corePoolSize,则该线程将会被回收;
        //  -> 核心线程会一直存活,除非线程池被关闭 或 设置下面的参数
        //  -> 如果 AllowCoreThreadTimeout设置为true,核心线程也会被回收,直到线程池中的线程数降为 0。
        //     但如果线程池中有任务在执行,那么空闲线程就会一直保持存活状态,直到任务执行完毕。
        //  -> 该方法的使用可以将线程池的空闲线程回收,以减少资源占用,同时也能保证线程池中始终有可用的线程来执行任务,提高线程池的效率。

⑥、是否禁止线程池自动终止空闲的核心线程

为 true 时,空闲的核心线程会在 keepAliveTime 时间后被回收,并且在后续任务到来时需要重新创建线程来执行任务。
        // 为 false 时,线程池中的核心线程不会被回收,即使它们处于空闲状态一段时间。
        //  -> 在线程池创建时,就会预先创建核心线程数的线程,这些线程将一直存在,除非线程池被关闭或重新配置。

⑦、当前线程池的等待时间

指等待所有任务执行完毕后线程池的最长时间。300秒 = 5分钟
        // -> 当所有任务执行完毕后,线程池会等待一段时间(即等待时间),来确保所有任务都已经完成。
        // -> 如果在等待时间内所有任务仍未完成,则线程池会强制停止,以确保任务不会无限制地执行下去。

⑧、当前线程池是否在关闭时等待所有任务执行完成

		// -> 可以确保所有任务都执行完毕后才关闭线程池,避免任务被丢弃,同时也确保线程池可以正常结束,释放资源。
        // -> 为 true 时,线程池在关闭时会等待所有任务都执行完成后再关闭
        // -> 为 false 时,线程池会直接关闭,未执行完成的任务将被丢弃。

⑨、线程名称前缀

		// 9线程前缀名称
        executor.setThreadNamePrefix("myIo-Th-Pool-");
        // 初始化
        executor.initialize();

2.2、合理配置

①线程数量:N = 计算机cpu数量

  • 如果同时运行的线程数量超过 CPU 核心数,就会发生--线程上下文切换--,导致额外的开销和性能下降。所以线程不能创建得过多
  • 一般的配置如下:也可以通过计算获取。
     * IO密级 :2 * N
     * CPU密级:1 + N

②队列长度:

  • 如果队列长度过大,可能会导致系统内存资源占用过高,最终导致 OOM,需要注意控制
  • 根据自身业务配置

3、配置

3.1、配置线程池的Bean的选择

配置线程池选择:ThreadPoolTaskExecutor(Spring项目推荐),还是选择ThreadPoolExecutor?

ThreadPoolTaskExecutor 是 Spring 框架中对 Java 自带的线程池 ThreadPoolExecutor 进行了封装和扩展,并增加了一些优化和功能。通常来说,如果你使用 Spring 框架,需要使用线程池,那么建议使用 ThreadPoolTaskExecutor。 ThreadPoolTaskExecutor 提供了更多的配置选项,例如线程池的最大线程数、核心线程数、缓冲队列大小、线程命名前缀、线程池饱和策略等等,同时可以方便地集成到 Spring 应用中。另外,ThreadPoolTaskExecutor 还能够支持异步执行任务,使用方便。 相比之下,ThreadPoolExecutor 是 Java 自带的线程池实现类,提供了基本的线程池功能,但没有 ThreadPoolTaskExecutor 提供的更多配置选项和功能。如果你不使用 Spring 框架,或者使用 Spring 框架但不需要使用其提供的线程池实现,那么可以考虑使用 ThreadPoolExecutor。

综上所述,选择使用 ThreadPoolTaskExecutor 还是 ThreadPoolExecutor 取决于具体的业务需求和技术栈,可以根据实际情况进行选择。

3.2、获取当前电脑(服务器)的核心线程数

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

3.3、IO密集型

package com.cc.md.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/** IO型的线程池
 * <li>IO密集型配置线程数经验值是:2N (CPU核数*2)</li>
 * <li>异步线程池:建议用io密集型:</li>
 *      对于异步线程池,通常建议使用IO密集型线程池。
 *      异步任务通常是网络IO或磁盘IO等操作,这些操作的执行时间相对于CPU计算的执行时间要长得多。
 *      使用IO密集型线程池可以更好地利用IO资源,提高多个异步任务的执行效率和吞吐量,
 *      同时避免由于过多的线程切换和上下文切换导致的性能损失。
 * @author CC
 * @since 2023/5/23 0023
 */
@Configuration
@EnableAsync
public class IoThreadPool {

    /** 线程数量
     * CUP数量:N = Runtime.getRuntime().availableProcessors()
     * IO密级:2 * N
     * CPU密级:1 + N
     */
    public static final int THREAD_SIZE = 2 * (Runtime.getRuntime().availableProcessors());
    /**
     * 队列大小
     */
    public static final int QUEUE_SIZE = 1000;

    @Bean(name = "myIoThreadPool")
    public ThreadPoolTaskExecutor threadPoolExecutor(){
        //配置线程池选择:ThreadPoolTaskExecutor,还是选择ThreadPoolExecutor好些?
        // -> ThreadPoolTaskExecutor 是 Spring 框架中对 Java 自带的线程池 ThreadPoolExecutor 进行了封装和扩展,
        //    并增加了一些优化和功能。通常来说,如果你使用 Spring 框架,需要使用线程池,那么建议使用 ThreadPoolTaskExecutor。
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 1核心线程数:当线程池中的线程数量为 corePoolSize 时,即使这些线程处于空闲状态,也不会销毁(除非设置 allowCoreThreadTimeOut=true)。
        //  -> 核心线程,也就是正在处理中的任务
        //  -> 虽然 CPU 核心数可以作为线程池中线程数量的参考指标,但最终线程数量还需要根据具体情况进行设置和调整。
        //  -> 如果同时运行的线程数量超过 CPU 核心数,就会发生--线程上下文切换--,导致额外的开销和性能下降。所以线程不能创建得过多
        executor.setCorePoolSize(THREAD_SIZE);
        // 2最大线程数:线程池中允许的线程数量的最大值。
        //  -> 当线程数 = maxPoolSize最大线程数时,还有新任务,就会放进队列中等待执行 ↓↓↓
        executor.setMaxPoolSize(THREAD_SIZE);
        // 3队列长度:当核心线程数达到最大时,新任务会放在队列中排队等待执行
        //  -> 根据业务配置,如果队列长度过大,可能会导致系统内存资源占用过高,最终导致 OOM,需要注意控制
        //  -> 如果需要执行的任务装满了队列,就会走拒绝策略 ↓↓↓
        executor.setQueueCapacity(QUEUE_SIZE);
        // 4拒绝策略(官方提供4种,也可以自定义):因达到线程边界和任务队列满时,针对新任务的处理方法。
        // -> AbortPolicy:直接丢弃任务并抛出 RejectedExecutionException 异常。(默认策略)
        // -> DiscardPolicy:直接丢弃掉,不会抛出异常
        // -> DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
        // -> CallerRunsPolicy:交给主线程(调用线程)去执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 5空闲线程存活时间(默认60s):设置当前线程池中空闲线程的存活时间,即线程池中的线程如果有一段时间没有任务可执行,则会被回收掉。
        //  -> 当线程池中的线程数大于 corePoolSize 时,多余的空闲线程将在销毁之前等待新任务的最长时间。
        //  -> 如果一个线程在空闲时间超过了 keepAliveSeconds,且当前线程池中线程数量大于 corePoolSize,则该线程将会被回收;
        //  -> 核心线程会一直存活,除非线程池被关闭 或 设置下面的参数
        //  -> 如果 AllowCoreThreadTimeout设置为true,核心线程也会被回收,直到线程池中的线程数降为 0。
        //     但如果线程池中有任务在执行,那么空闲线程就会一直保持存活状态,直到任务执行完毕。
        //  -> 该方法的使用可以将线程池的空闲线程回收,以减少资源占用,同时也能保证线程池中始终有可用的线程来执行任务,提高线程池的效率。
        executor.setKeepAliveSeconds(60);
        //6是否禁止线程池自动终止空闲的核心线程。
        // 为 true 时,空闲的核心线程会在 keepAliveTime 时间后被回收,并且在后续任务到来时需要重新创建线程来执行任务。
        // 为 false 时,线程池中的核心线程不会被回收,即使它们处于空闲状态一段时间。
        //  -> 在线程池创建时,就会预先创建核心线程数的线程,这些线程将一直存在,除非线程池被关闭或重新配置。
        executor.setAllowCoreThreadTimeOut(true);
        // 7当前线程池的等待时间:指等待所有任务执行完毕后线程池的最长时间。300秒 = 5分钟
        // -> 当所有任务执行完毕后,线程池会等待一段时间(即等待时间),来确保所有任务都已经完成。
        // -> 如果在等待时间内所有任务仍未完成,则线程池会强制停止,以确保任务不会无限制地执行下去。
        executor.setAwaitTerminationSeconds(300);
        // 8当前线程池是否在关闭时等待所有任务执行完成
        // -> 可以确保所有任务都执行完毕后才关闭线程池,避免任务被丢弃,同时也确保线程池可以正常结束,释放资源。
        // -> 为 true 时,线程池在关闭时会等待所有任务都执行完成后再关闭
        // -> 为 false 时,线程池会直接关闭,未执行完成的任务将被丢弃。
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 9线程前缀名称
        executor.setThreadNamePrefix("myIo-Th-Pool-");
        // 初始化
        executor.initialize();
        return executor;
    }
}

3.4、CPU密集型

package com.cc.md.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

/** CPU型的线程池
 * @author CC
 * @since 2023/5/23 0023
 */
@Configuration
public class CpuThreadPool {

    /** 线程数量
     * CUP数量:N = Runtime.getRuntime().availableProcessors()
     * IO密级:2 * N
     * CPU密级:1 + N
     */
    public static final int THREAD_SIZE = 1 + (Runtime.getRuntime().availableProcessors());
    /**
     * 队列大小
     */
    public static final int QUEUE_SIZE = 1000;

    @Bean(name = "myCpuThreadPool")
    public ThreadPoolTaskExecutor threadPoolExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(THREAD_SIZE);
        executor.setMaxPoolSize(THREAD_SIZE);
        executor.setQueueCapacity(QUEUE_SIZE);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        executor.setKeepAliveSeconds(60);
        executor.setAllowCoreThreadTimeOut(true);
        executor.setAwaitTerminationSeconds(300);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setThreadNamePrefix("myCpu-T-Pool-");
        executor.initialize();
        return executor;
    }
}

4、参考

https://zhuanlan.zhihu.com/p/112527671

https://blog.csdn.net/shang_0122/article/details/120777113

https://blog.csdn.net/zhuimeng_by/article/details/107891268

https://blog.csdn.net/qq_25720801/article/details/129559164

https://blog.csdn.net/riemann_/article/details/104704197

标签:Java,配置,任务,线程,IO,executor,执行,CPU
From: https://www.cnblogs.com/kakarotto-chen/p/17428432.html

相关文章

  • 流程表单JavaScript代码
    ----订单流程-----------//表单加载初始化时functionpreinit(){}//表单加载完成,isrun代表流程是否流转中1-是,0-否functionLoaded(isrun){$("#om_order_status").attr("disabled","disabled");......
  • java入门2..0
    java的运行原理1.在本地磁盘中创建一个文本文件为Demo.java的源文件2.在源文件中编写java代码如下:publicclassDemopublicstaticvoid,main(String[]args){System.out.println(" ");}3.在当前文件目录下。输入cmd跳转到dos窗口4.通过java编译源文件。会生成字节码文件......
  • 剑指 Offer II 018(Java). 有效的回文(简单)
    题目:给定一个字符串s,验证s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。本题中,将空字符串定义为有效的 回文串 。 示例1:输入:s="Aman,aplan,acanal:Panama"输出:true解释:"amanaplanacanalpanama"是回文串示例2:输入:s="raceacar"......
  • 【JavaWeb-02】Web服务器
    文章目录2.web服务器2.1技术讲解2.2web服务器2.web服务器2.1技术讲解JSP/Servlet:B/S:浏览和服务器C/S:客户端和服务端sun公司主推的B/S架构基于Java语言的(所有的大公司,或者一些开源的组件,都是用Java写的)可以承载三高问题带来的影响2.2web服务器IIS:微软的Tmocat:Java初学人员......
  • 京东数科java一面【过】
    自我介绍实习经历【聊了挺多】集合方面Collection【list(写时复制)/set/Queue(阻塞队列)】Map【HashTable/HashMap/ConcorrentHashMap】synchronized对象头原理的过程A系统1WTBS,B系统300TBS,问解决方法技术角度:读写分离、Kong层、加服务器业务角度:加锁、加进度条AQS是什么原理Volat......
  • 【Kafka从入门到成神系列 二】Kafka集群参数配置
    ......
  • 阿里蚂蚁集团Java一面【凉】
    一个小哥哥打来的电话1.自我介绍2.介绍实习实习的时候用到了分布式锁深挖分布式锁的实现【回去复盘】遇到了什么问题?为什么用这个?怎么用的?怎么实现?3.多服务器之间是怎么保持数据一致的【回去复盘】4.分布式事务5.微服务了解嘛【回去复盘】6.MySQL的索引7.MySQL的乐观锁......
  • 【从Java转C#】第三章:对象和类型
    目录对象和类型ref和out参数的使用方法的重载属性构造函数匿名类型结构【Struct】弱引用(WeakReference)静态类Object对象和类型ref和out双方都可以改变原始的地址初始值的不同ref:需要赋予变量初始值out:不需要赋予变量初始值namespaceConsoleApp1{classProgram{......
  • C#开发环境配置-VS2017安装使用
    工欲善其事,必先利其器传说中的世界第一编辑器目录1.下载2.安装2.1点击下图2.2进行解析2.3启动3.自己的第一个程序4.问题1.下载资源是楼主花钱在淘宝买的,现在免费送给大家关注公众号”爱敲代码的小黄“,回复:VS2017,即可收到网盘链接 2.安装2.1点击下图2.2进行解析进度条加......
  • 【Kafka从入门到成神系列 八】Kafka 多线程消费者及TCP连接
    ......