Spring(19) ThreadPoolTaskExecutor 线程池的使用

目录

    • 一、线程池简介
      • 1.1 为什么使用线程池
      • 1.2 线程池为什么需要使用队列
      • 1.3 线程池为什么要使用阻塞队列而不是用非阻塞队列
      • 1.4 如何配置线程池
      • 1.5 execute() 和 submit() 方法
    • 二、ThreadPoolTaskExecutor 线程池简介
      • 2.1 简介
      • 2.2 核心参数配置
      • 2.3 ThreadPoolTaskExecutor 内部执行流程
    • 三、ThreadPoolTaskExecutor 的代码示例
      • 3.1 配置线程池示例
      • 3.2 使用线程池示例
      • 3.3 执行结果

一、线程池简介

1.1 为什么使用线程池

  • 降低系统资源消耗: 通过重用已存在的线程,降低线程创建和销毁造成的消耗;
  • 提高系统响应速度: 当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行;
  • 方便线程并发数的管控: 因为线程若是无限制的创建,可能会导致内存占用过多而产生 OOM,并且会造成 CPU 过度切换(CPU 切换线程是有时间成本的,需要保持当前执行线程的现场,并恢复要执行线程的现场);
  • 提供更强大的功能: 延时定时线程池。

1.2 线程池为什么需要使用队列

因为线程若是无限制的创建,可能会导致 内存 占用过多而产生 OOM,并且会造成 CPU 过度切换。

创建线程池的消耗较高,或者线程池创建线程需要获取 mainlock 这个全局锁,影响并发效率,阻塞队列可以很好的缓冲。

1.3 线程池为什么要使用阻塞队列而不是用非阻塞队列

阻塞队列 可以保证任务队列中没有任务是阻塞获取任务的线程,使得线程进入 wait 状态,释放 CPU 资源,当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。

使得线程不至于一直占用 CPU 资源。(线程执行完任务后通过循环再次从任务队列中取出任务进行执行,代码片段如:while (task != null || (task = getTask()) != null) {})。

不用阻塞队列也是可以的,不过实现起来比较麻烦而已,有好用的为啥不用呢?

1.4 如何配置线程池

  • CPU密集型任务:

    尽量使用较小的线程池,一般为 CPU 核心数 +1。因为 CPU密集型任务使得 CPU 使用率很高,若开很多的线程数,会造成 CPU 过度切换。

  • IO密集型任务:

    可以使用较大的线程池,一般为 2*CPU 核心数。IO密集型任务 CPU 使用率并不高,因此可以让 CPU 在等待 IO 的时候有其他线程去处理别的任务,充分利用 CPU 时间。

  • 混合型任务:

    可以将任务分为 IO密集型和 CPU密集型任务,然后分别用不同的线程池去处理。只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。

    因为如果划分之后两个任务执行时间有数据级别的差距,那么拆分没有意义。因为先执行完的任务就要等候执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失。

1.5 execute() 和 submit() 方法

  • execute():执行一个任务,没有返回值。
  • submit():提交一个任务,有返回值。

submit() 方法的使用如下:

  • submit(Callable<T> task): 能够获取到它的返回值,通过 feature.get() 获取(阻塞直到任务执行完)。一般使用 FutureTask + Callable 配合使用。
  • submit(Runnable task, T result):能通过传入的载体 result 间接获得线程的返回值。
  • submit(Runnable task):则是没有返回值的,就算获取它的返回值也是 null

future.get() 方法会使获取结果的线程进入 阻塞状态,直到线程执行完成之后,唤醒获取结果的线程,然后返回结果。


二、ThreadPoolTaskExecutor 线程池简介

2.1 简介

  • ThreadPoolTaskExecutor 是 Spring Framework 提供的一个线程池执行器,它基于 java.util.concurrent 包中的 ThreadPoolExecutor 实现,并提供了更方便的 Spring 配置和生命周期管理。

在 Spring 应用程序中,你可以通过配置一个 ThreadPoolTaskExecutor 来创建并管理一个自定义的线程池。这个线程池可以根据你的需求设置 核心线程数最大线程数队列容量线程存活时间 以及 拒绝策略 等属性。

2.2 核心参数配置

  • corePoolSize最小线程数,默认为 1
  • maxPoolSize最大线程数,默认为 Integer.MAX_VALUE
  • keepAliveSeconds(maxPoolSize-corePoolSzie)部分线程空闲最大存活时间,默认存活时间是 60s
  • queueCapacity阻塞队列的大小,默认为 Integer.MAX_VALUE,默认使用 LinkedBlockingQueue
  • allowCoreThreadTimeOut是否允许核心线程过期,设置为 true 的话,keepAliveSeconds 参数设置的有效时间对 corePoolSize 线程也有效,默认是 false
  • threadNamePrefix线程名称前缀,为 ThreadPoolTaskExecutor 的增强功能,默认为“类名-”。
  • threadFactory设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。使用开源框架 guava 提供的 ThreadFactoryBuilder 可以快速给线程池里的线程设置有意义的名字。
  • rejectedExecutionHandler拒绝策略,当队列 workQueue 和线程池 maxPoolSize 都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是 AbortPolicy,表示无法处理新任务时抛出异常。

2.3 ThreadPoolTaskExecutor 内部执行流程

在这里插入图片描述

我们通过 execute(Runnable) 方法或者 submit(Runnable) 方法将 Runnable 任务添加到线程池时:

  • 如果线程池中的数量小于 corePoolSize,即使线程池中的线程都处于空闲状态,也要 创建新的线程来处理被添加的任务
  • 如果线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue 未满,那么 任务被放入缓冲队列
  • 如果线程池中的数量大于 corePoolSize,缓冲队列 workQueue 满了,并且:
    • 线程池中的数量小于 maxPoolSize,则 创建新的线程来处理被添加的任务
    • 线程池中的数量等于 maxPoolSize,则 通过 handler 所指定的拒绝策略来处理被添加的任务

也就是说,处理任务的优先级为:

核心线程corePoolSize > 任务队列workQueue > 最大线程maxPoolSize > 拒绝策略handler


三、ThreadPoolTaskExecutor 的代码示例

3.1 配置线程池示例

ThreadPoolConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;/*** <p> @Title ThreadPoolConfig* <p> @Description 线程池配置** @author ACGkaka* @date 2024/01/18 21:08*/
@Configuration
public class ThreadPoolConfig {/** 最佳线程数:操作系统线程数+2 */private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 2;/** 最大线程数 */private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 2;/** 队列长度 */private static final int QUEUE_CAPACITY = 10000;/*** 发送短信线程池*/@Beanpublic ThreadPoolTaskExecutor sendSMSThreadPool() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CORE_POOL_SIZE);executor.setMaxPoolSize(MAX_POOL_SIZE);executor.setQueueCapacity(QUEUE_CAPACITY);executor.setThreadNamePrefix("SendSMS-");// 等待任务执行完毕后再关闭线程池executor.setWaitForTasksToCompleteOnShutdown(true);executor.initialize();return executor;}
}

3.2 使用线程池示例

import com.demo.service.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** <p> @Title DemoServiceImpl* <p> @Description 测试ServiceImpl** @author ACGkaka* @date 2023/4/24 18:14*/
@Slf4j
@Service
public class DemoServiceImpl implements DemoService {@Resourceprivate ThreadPoolTaskExecutor sendSMSThreadPool;@Overridepublic void sendSMS() {// 线程池-发送短信sendSMSThreadPool.execute(() -> {log.info("Thread: {}, SMS sending...", Thread.currentThread().getName());});}
}

3.3 执行结果

请求后,日志打印结果如下:

(可以看到线程名称前缀已成功生效。)

在这里插入图片描述

整理完毕,完结撒花~ 🌻





参考地址:

1.Spring线程池ThreadPoolTaskExecutor的使用,https://blog.csdn.net/u012060033/article/details/111934507

2.线程池ThreadPoolTaskExcutor详解,https://juejin.cn/post/7073459521691222024

3.Java线程池详解,https://blog.csdn.net/weixin_40096160/article/details/130542750

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/632500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库作业三

1.创建student和score表 2.为student表和score表增加记录 3.查询student表的所有记录 4.查询student表的第2条到4条记录 5.从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&#xff09;和院系&#xff08;department&#xff09;的信息 6.从st…

Pandas实战100例 | 案例 58: 创建数据透视表

案例 58: 创建数据透视表 知识点讲解 数据透视表是一种常用的数据汇总工具,它允许你在多个维度上重塑和汇总数据。Pandas 提供了 pivot_table 方法来创建数据透视表。 创建数据透视表: 使用 pivot_table 方法,你可以指定值、索引、列和聚合函数来创建数据透视表。这使得数据…

React导航守卫(V5路由)

下载&#xff1a; npm i react-router-dom5 当我们进行路由跳转的时候&#xff0c;有时候需要满足某种条件才能跳转&#xff0c;比如我只有我们登录成功之后才能到首页面&#xff0c;否则就不能到首页面&#xff0c;这时候我们就需要对路由进行拦截。 例如&#xff1a; (1)…

flutter3使用dio库发送FormData数据格式时候的坑,和get库冲突解决办法

问题描述 问题1&#xff1a;当你使用FormData.from(Flutter3直接不能用)的时候&#xff0c;可能会提示没有这个方法&#xff0c;或者使用FormData.fromMap(flutter3的dio支持)的时候也提示没有&#xff0c;这时候可能就是和get库里面的Formdata冲突了 问题1&#xff1a;The me…

C //练习 8-2 用字段代替显式的按位操作,重写fopen和_fillbuf函数。比较相应代码的长度和执行速度。

C程序设计语言 &#xff08;第二版&#xff09; 练习 8-2 练习 8-2 用字段代替显式的按位操作&#xff0c;重写fopen和_fillbuf函数。比较相应代码的长度和执行速度。 注意&#xff1a;代码在win32控制台运行&#xff0c;在不同的IDE环境下&#xff0c;有部分可能需要变更。 …

easydarwin 下面easywasmplayer和easyplayer

拷贝大佬们&#xff0c;起码验证能用再写文章&#xff0c;找到的各种坑 easydarwin/easywasmplayer - npm 区别 easyplayer&#xff1a;在与vue2结合的时候没有问题&#xff0c;但是用在vue3中&#xff0c;就是提示各种缺东西&#xff0c;你就是将所有文件引入到index.html都…

github clone Failed to connect to github.com port 443 after xxx ms

最近克隆github项目时老是报超时&#xff0c;可以尝试以下解决方法 如果本地开启了代理还是clone超时&#xff0c;可以尝试最后一种方式解决 1、把 https 换成 http&#xff0c;如&#xff1a; git clone http:xxx2、更新本地hosts配置&#xff0c;可以参考这篇文章获取最新的…

vue基于spring boot框架的发艺美发店理发店管理系统的设计q9xpe

店铺信息、美发信息是发艺美发店管理系统的重要组成部分&#xff0c;信息清晰、详细、准确&#xff0c;能够有效地促进发艺美发店管理系统的运行[5]。基础设定函数是对整个系统的总体布局进行合理安排&#xff0c;包括&#xff1a;店铺活动、物品信息、领用信息等。通过对各类资…

【C++】vector模拟实现过程中值得注意的点

Hello大家好&#xff01;我是咕噜的铁蛋&#xff01;C中的vector是一种动态数组&#xff0c;它能够根据需要自动增长和缩小。虽然C标准库已经为我们提供了vector的实现&#xff0c;但在某些情况下&#xff0c;我们可能需要自己模拟实现一个类似于vector的数据结构。今天铁蛋将给…

CPU密集型计算、IO密集型计算、多进程、多线程

参考链接&#xff1a; 使用多进程multiprocessing模块加速程序的运行_哔哩哔哩_bilibili 什么是CPU密集型计算、IO密集型计算&#xff1a; CPU密集型&#xff1a; CPU密集型也叫计算密集型&#xff0c;是指I/O在很短的时间就可以完成&#xff0c;CPU需要大量的计算和处理&a…

javaweb学习day01(HTML)

一、B/S 软件开发架构简述 1 Java Web 技术体系图 2 B/S 软件开发架构简述 B/S架构 前端 后端 数据库 二、HTML 1 官方文档 地址: https://www.w3school.com.cn/html/index.asp 离线文档: W3School 离线手册(2017.03.11 版).chm 2 网页 3 HTML 介绍 3.1 HTML 是什么…

oop面向对象编程python

1.定义一个圆类&#xff08;Circle&#xff09;,求圆的面积和周长 import mathclass Circle():def __init__(self, R ,name):self.radius Rself.name namedef girth(self):return 2 * self.radius * math.pidef area(self):return self.radius ** 2 * math.pic1 Circle(10,&q…

python 常用功能积累

sql查询 import pymysql import pandas as pduser #用户名 password #密码 dbName #库名 dbHost #ip dbPort 8888 con pymysql.connect(hostdbHost,portdbPort,useruser,passwordpassword,databasedbName,charsetutf8) cursor con.cursor() head ["Id"…

React16源码: React中调度之requestWork的源码实现

requestWork 1 &#xff09;概述 在 scheduleWork 中&#xff0c;找到了创建更新的fiber对应的root节点然后对它进行了一些操作之后&#xff0c;调用了 requestWork&#xff0c;开始请求工作在 requestWork 里面它会做哪些东西呢&#xff1f; 首先我们要把这个root节点加入到调…

Qt所有容器之间的区别,以及如何简单使用?

Qt中常用的容器有 QList、QVector、QMap、QHash、QSet 等,它们都具有不同的特性和适用场景。 1. QList:动态数组,可以动态增加和删除元素,支持随机访问和迭代,适用于不需要频繁插入或删除元素的场景。 ```cpp QList<int> list; list.append(1); list.append(2); fo…

Docker之nacos集群部署

前言 Nacos 是一个开源的注册中心和配置中心&#xff0c;用于实现微服务架构中的服务发现、服务治理和动态配置管理。在 Docker 中使用 Nacos&#xff0c;你可以通过拉取官方提供的 Docker 镜像并运行容器的方式来快速部署 一.Nacos镜像拉取及独立模式 镜像拉取命令详解&…

【自动化测试】看完这篇文章,让你了解到你和大厂的差距到底在哪儿

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

基于STM32F103的病房监控系统的设计

论文题目&#xff1a;基于STM32F103的病房监控系统的设计 摘要&#xff1a; 病房是患者恢复期间的重要环境&#xff0c;对于病房内的环境和设备状态进行监测和管理具有重要意义。本文提出了一种基于STM32F103的病房监控系统的设计方案。该方案利用嵌入式系统和物联网技术&…

sc.pl.umap 画feature plot

今天有时间尝试测试了这个scanpy的feature plot,其实很简单&#xff0c;就是使用 sc.pl.umap(adata,color"gene name"), 但是这个地方就有一个问题&#xff0c;这个画出来的值是原始的基因值还是scale之后的&#xff0c;这个我得搞清楚 首先看使用例子&#xff0c;参…

企业为什么需要选择软件测试外包公司?有哪些查找途径?

软件测试外包公司是专门为软件企业提供软件测试服务的机构。这些公司拥有一支经验丰富、专业化的软件测试团队&#xff0c;能够帮助企业进行软件产品的测试、评估和质量保证。 一、企业为什么需要选择软件测试外包公司?   1.专业的测试团队&#xff1a;外包公司拥有经验丰富…