一、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配置线程池的更多相关文章

  1. 深入理解Java之线程池

    原作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本文归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则 ...

  2. Java(Android)线程池 总结

        JAVA的Executors源码:(可以看出底层都是通过ThreadPoolExecutor来具体设置的~) public static ExecutorService newCachedTh ...

  3. 【java】-- 线程池原理分析

    1.为什么要学习使用多线程? 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担. 线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致 ...

  4. Java队列——线程池创建的例子

    线程池为线程生命周期开销问题和资源不足问题提供了解决方案.通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上.其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟.这 ...

  5. 深入理解Java之线程池(爱奇艺面试)

    爱奇艺的面试官问 (1) 线程池是如何关闭的 (2) 如何确定线程池的数量 一.线程池销毁,停止线程池 ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown() ...

  6. [转]深入理解Java之线程池

    原文链接 原文出处: 海 子 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这 ...

  7. 【java多线程】java的线程池分析

    (一)线程池的拒绝策略 --->拒绝策略的接口java.util.concurrent.RejectedExecutionHandler --->终止策略(默认):java.util.co ...

  8. 沉淀再出发:java中线程池解析

    沉淀再出发:java中线程池解析 一.前言 在多线程执行的环境之中,如果线程执行的时间短但是启动的线程又非常多,线程运转的时间基本上浪费在了创建和销毁上面,因此有没有一种方式能够让一个线程执行完自己的 ...

  9. Java并发--线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

  10. 第九章 Java中线程池

    Java中的线程池是运用场景最多的并发框架,几乎所有需求异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 降低资源消耗:通过重复利用已创建的线程降低线程创建和 ...

随机推荐

  1. Linux & 标准C语言学习 <DAY8_2>

    一.函数 Function     一段具有某一项功能的代码集合,是C语言管理代码的最小单位     把代码封装成一个个函数,方便管理和调用函数     1.函数分类         标准库函数:   ...

  2. 网络----OSI七层

    OSI 订制的是一个用于计算机或通信系统间互联的标准体质(一般称为OSI参考模型或七层模型) OSI 模型把网络通信的工作分为7层分别是: 常用内容:物理层 数据链层 网络层 注解 OSI 7层 1. ...

  3. monggodb项目操作

    1.回顾 1.express + node 1.1 准备工作 前后端不分离开发 --- 前端负责写页面,后端负责渲染 --- admin-lte 1.2 创建express项目 express mya ...

  4. Linux磁盘LVM根目录扩容

    LVM 的基本概念 物理卷 Physical Volume (PV):可以在上面建立卷组的媒介,可以是硬盘分区,也可以是硬盘本身或者回环文件(loopback file).物理卷包括一个特殊的 hea ...

  5. flask+gunicorn+nginx部署pytorch/python应用

    1. 基于flask实现python服务Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 .Flask使用 ...

  6. python类详解

    类封装 继承 多态一静态属性1.静态变量和静态方法都属于类的静态成员,它们与普通的成员变量和成员方法不同,静态变量和静态方法只属于定义它们的类,而不属于某一个对象.2.静态变量和静态方法都可以通过类名 ...

  7. 五月十一号java基础知识点

    1.通过add()方法向链表list输入1-10十个数for (int i = 1; i <11 ; i++) { list.add(i);//向链表添加1-10的整数 } import jav ...

  8. subprocess,哈希,日志模块

    hashlib模块: # 1. 先确定你要使用的加密方式: md系列,sha系列 md5 = hashlib.md5() # 指定加密方式 # 2. 进行明文数据的加密 data = 'hello12 ...

  9. day26:装饰器&面向对象当中的方法&property

    目录 1.装饰器 1.1 装饰器的基本用法 1.2 @符号的使用 1.3 装饰器的嵌套 1.4 用装饰器扩展带有参数的原函数 1.5 用装饰器扩展带有参数和返回值的原函数 1.6 用类装饰器扩展原函数 ...

  10. day18:json模块&time模块&zipfile模块

    json模块 1.关于json的定义 所有的编程语言都能够识别的数据格式叫做json,是字符串能够通过json序列化成字符串与如下类型: (int float bool str list tuple ...