Java配置线程池
一、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配置线程池的更多相关文章
- 深入理解Java之线程池
原作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本文归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则 ...
- Java(Android)线程池 总结
JAVA的Executors源码:(可以看出底层都是通过ThreadPoolExecutor来具体设置的~) public static ExecutorService newCachedTh ...
- 【java】-- 线程池原理分析
1.为什么要学习使用多线程? 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担. 线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致 ...
- Java队列——线程池创建的例子
线程池为线程生命周期开销问题和资源不足问题提供了解决方案.通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上.其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟.这 ...
- 深入理解Java之线程池(爱奇艺面试)
爱奇艺的面试官问 (1) 线程池是如何关闭的 (2) 如何确定线程池的数量 一.线程池销毁,停止线程池 ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown() ...
- [转]深入理解Java之线程池
原文链接 原文出处: 海 子 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这 ...
- 【java多线程】java的线程池分析
(一)线程池的拒绝策略 --->拒绝策略的接口java.util.concurrent.RejectedExecutionHandler --->终止策略(默认):java.util.co ...
- 沉淀再出发:java中线程池解析
沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...
- Java并发--线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...
- 第九章 Java中线程池
Java中的线程池是运用场景最多的并发框架,几乎所有需求异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 降低资源消耗:通过重复利用已创建的线程降低线程创建和 ...
随机推荐
- 制作微软原版Windows11 PE(含Powershell)
1.adksetup下载链接:https://download.microsoft.com/download/1/f/d/1fd2291e-c0e9-4ae0-beae-fbbe0fe41a5a/ad ...
- 常用Linux发行版操作系统大盘点
B站CodeSheep的教程 https://www.bilibili.com/read/cv6026694
- Java面试——数据库
一.数据库隔离级别 [1]Read Uncommitted(读取未提交内容):出现脏读,也就是可能读取到其他会话中未提交事务修改的数据.[2]Read Committed(读取已提交内容):不可重复读 ...
- 机器学习算法(八):基于BP神经网络的乳腺癌的分类预测
机器学习算法(八):基于BP神经网络的乳腺癌的分类预测 1.算法简介和应用 1.1 算法简介 BP(Back Propagation)网络是1986年由Rumelhart和McCelland为首的科学 ...
- GitHub+Typora实现云笔记一键上传
git实现笔记自动上传功能 简介: 将更新内容自动上传同步git,无需手动提交,解锁一键式同步.流程大致为,创建新仓库,配置公钥和私钥,安装quicker软件,通过quicker上某脚本完成一键提交. ...
- 迁移学习(DCCL)《Domain Confused Contrastive Learning for Unsupervised Domain Adaptation》
论文信息 论文标题:Domain Confused Contrastive Learning for Unsupervised Domain Adaptation论文作者:Quanyu Long, T ...
- python-爬虫-css提取-写入csv-爬取猫眼电影榜单
猫眼有一个电影榜单top100,我们将他的榜单电影数据(电影名.主演.上映时间.豆瓣评分)抓下来保存到本地的excle中 本案例使用css方式提取页面数据,所以会用到以下库 import time i ...
- PySimpleGU之运行多个窗口
这是PySimpleGUI继续简单的地方,但是问题空间刚刚进入了"复杂"领域. 如果您希望在事件循环中运行多个窗口,则有两种方法可以执行此操作. 当第二个窗口可见时,第一个窗口不会 ...
- LeeCode 90双周赛复盘
T1: 差值数组不同的字符串 思路:数组遍历 若前两个字符串差值数组不同,则只需要继续计算第三个字符串的差值数组即可得到答案 若前两个字符串差值数组相同,则依次遍历后续字符串,直至找到不同的差值数组 ...
- 如何优雅的使用ipv6穿透内网
背景 随着ipv6的普及,家庭宽带已经全面支持ipv6,通过简单的设置就可以让自己的内网设备获取到ipv6地址.不过这里的ipv6地址也不是固定,会定期的变化,不过通过DDNS可以解决这个问题.但是这 ...