论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
应用中心
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
qidao123.com技术社区-IT企服评测·应用市场
»
论坛
›
软件与程序人生
›
后端开发
›
Java
›
Java配置线程池
Java配置线程池
南飓风
论坛元老
|
2023-5-24 17:03:02
|
显示全部楼层
|
阅读模式
楼主
主题
1895
|
帖子
1895
|
积分
5685
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
一、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型的线程池 * IO密集型配置线程数经验值是:2N (CPU核数*2)
* 异步线程池:建议用io密集型:
* 对于异步线程池,通常建议使用IO密集型线程池。 * 异步任务通常是网络IO或磁盘IO等操作,这些操作的执行时间相对于CPU计算的执行时间要长得多。 * 使用IO密集型线程池可以更好地利用IO资源,提高多个异步任务的执行效率和吞吐量, * 同时避免由于过多的线程切换和上下文切换导致的性能损失。 * @author CC * @since 2023/5/23 0023 */@Configuration@EnableAsyncpublic 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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
南飓风
论坛元老
这个人很懒什么都没写!
楼主热帖
零信任介绍
容斥原理
开源SPL助力JAVA处理公共数据文件(txt ...
使用 Helm 安装 MQTT 服务器-EMQX ...
数理逻辑第1-3章
Ubuntu如何安装Mysql+启用远程连接[完 ...
dotnet 修复在 Linux 上使用 SkiaSharp ...
DOS窗口命令和单表简单查询
Java笔记(13) 简单的Lambda表达式 ...
.gitignore文件配置以及gitee提交报Pus ...
标签云
渠道
国产数据库
集成商
AI
运维
CIO
存储
服务器
浏览过的版块
Nosql
前端开发
快速回复
返回顶部
返回列表