Spring使用ThreadPoolTaskExecutor自定义线程池及异步调用方式
作者:create17 发布时间:2022-07-22 21:53:32
多线程一直是工作或面试过程中的高频知识点,今天给大家分享一下使用 ThreadPoolTaskExecutor 来自定义线程池和实现异步调用多线程。
一、ThreadPoolTaskExecutor
本文采用 Executors 的工厂方法进行配置。
1、将线程池用到的参数定义到配置文件中
在项目的 resources 目录下创建 executor.properties 文件,并添加如下配置:
# 异步线程配置
# 核心线程数
async.executor.thread.core_pool_size=5
# 最大线程数
async.executor.thread.max_pool_size=8
# 任务队列大小
async.executor.thread.queue_capacity=2
# 线程池中线程的名称前缀
async.executor.thread.name.prefix=async-service-
# 缓冲队列中线程的空闲时间
async.executor.thread.keep_alive_seconds=100
2、Executors 的工厂配置
2.1、配置详情
@Configuration
// @PropertySource是找的target目录下classes目录下的文件,resources目录下的文件编译后会生成在classes目录
@PropertySource(value = {"classpath:executor.properties"}, ignoreResourceNotFound=false, encoding="UTF-8")
@Slf4j
public class ExecutorConfig {
@Value("${async.executor.thread.core_pool_size}")
private int corePoolSize;
@Value("${async.executor.thread.max_pool_size}")
private int maxPoolSize;
@Value("${async.executor.thread.queue_capacity}")
private int queueCapacity;
@Value("${async.executor.thread.name.prefix}")
private String namePrefix;
@Value("${async.executor.thread.keep_alive_seconds}")
private int keepAliveSeconds;
@Bean(name = "asyncTaskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
log.info("启动");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(corePoolSize);
// 最大线程数
executor.setMaxPoolSize(maxPoolSize);
// 任务队列大小
executor.setQueueCapacity(queueCapacity);
// 线程前缀名
executor.setThreadNamePrefix(namePrefix);
// 线程的空闲时间
executor.setKeepAliveSeconds(keepAliveSeconds);
// 拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 线程初始化
executor.initialize();
return executor;
}
}
2.2、注解说明
@Configuration
:Spring 容器在启动时,会加载带有 @Configuration 注解的类,对其中带有 @Bean 注解的方法进行处理。@Bean
:是一个方法级别上的注解,主要用在 @Configuration 注解的类里,也可以用在 @Component 注解的类里。添加的 bean 的 id 为方法名。@PropertySource
:加载指定的配置文件。value 值为要加载的配置文件,ignoreResourceNotFound 意思是如果加载的文件找不到,程序是否忽略它。默认为 false 。如果为 true ,则代表加载的配置文件不存在,程序不报错。在实际项目开发中,最好设置为 false 。如果 application.properties 文件中的属性与自定义配置文件中的属性重复,则自定义配置文件中的属性值被覆盖,加载的是 application.properties 文件中的配置属性。@Slf4j
:lombok 的日志输出工具,加上此注解后,可直接调用 log 输出各个级别的日志。@Value
:调用配置文件中的属性并给属性赋予值。
2.3、线程池配置说明
核心线程数:线程池创建时候初始化的线程数。当线程数超过核心线程数,则超过的线程则进入任务队列。
最大线程数:只有在任务队列满了之后才会申请超过核心线程数的线程。不能小于核心线程数。
任务队列:线程数大于核心线程数的部分进入任务队列。如果任务队列足够大,超出核心线程数的线程不会被创建,它会等待核心线程执行完它们自己的任务后再执行任务队列的任务,而不会再额外地创建线程。举例:如果有20个任务要执行,核心线程数:10,最大线程数:20,任务队列大小:2。则系统会创建18个线程。这18个线程有执行完任务的,再执行任务队列中的任务。
线程的空闲时间:当 线程池中的线程数量 大于 核心线程数 时,如果某线程空闲时间超过 keepAliveTime ,线程将被终止。这样,线程池可以动态的调整池中的线程数。
拒绝策略:如果(总任务数 - 核心线程数 - 任务队列数)-(最大线程数 - 核心线程数)> 0 的话,则会出现线程拒绝。举例:( 12 - 5 - 2 ) - ( 8 - 5 ) > 0,会出现线程拒绝。线程拒绝又分为 4 种策略,分别为:
CallerRunsPolicy()
:交由调用方线程运行,比如 main 线程。AbortPolicy()
:直接抛出异常。DiscardPolicy()
:直接丢弃。DiscardOldestPolicy()
:丢弃队列中最老的任务。
2.4、线程池配置个人理解
当一个任务被提交到线程池时,首先查看线程池的核心线程是否都在执行任务。如果没有,则选择一条线程执行任务。
如果都在执行任务,查看任务队列是否已满。如果不满,则将任务存储在任务队列中。核心线程执行完自己的任务后,会再处理任务队列中的任务。
如果任务队列已满,查看线程池(最大线程数控制)是否已满。如果不满,则创建一条线程去执行任务。如果满了,就按照策略处理无法执行的任务。
二、异步调用线程
通常 ThreadPoolTaskExecutor 是和 @Async 一起使用。在一个方法上添加 @Async 注解,表明是异步调用方法函数。
@Async 后面加上线程池的方法名或 bean 名称,表明异步线程会加载线程池的配置。
@Component
@Slf4j
public class ThreadTest {
/**
* 每10秒循环一次,一个线程共循环10次。
*/
@Async("asyncTaskExecutor")
public void ceshi3() {
for (int i = 0; i <= 10; i ) {
log.info("ceshi3: " i);
try {
Thread.sleep(2000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
备注:一定要在启动类上添加 @EnableAsync 注解,这样 @Async 注解才会生效。
三、多线程使用场景
1、定时任务 @Scheduled
// 在启动类上添加 @EnableScheduling 注解
@SpringBootApplication
@EnableScheduling
public class SpringBootStudyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootStudyApplication.class, args);
}
}
// @Component 注解将定时任务类纳入 spring bean 管理。
@Component
public class listennerTest3 {
@Autowired
private ThreadTest t;
// 每1分钟执行一次ceshi3()方法
@Scheduled(cron = "0 0/1 * * * ?")
public void run() {
t.ceshi3();
}
}
ceshi3() 方法调用线程池配置,且异步执行。
@Component
@Slf4j
public class ThreadTest {
/**
* 每10秒循环一次,一个线程共循环10次。
*/
@Async("asyncTaskExecutor")
public void ceshi3() {
for (int i = 0; i <= 10; i ) {
log.info("ceshi3: " i);
try {
Thread.sleep(2000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2、程序一启动就异步执行多线程
通过继承 CommandLineRunner 类实现。
@Component
public class ListennerTest implements CommandLineRunner {
@Autowired
private ThreadTest t;
@Override
public void run(String... args) {
for (int i = 1; i <= 10; i ) {
t.ceshi();
}
}
}
@Component
@Slf4j
public class ThreadTest {
@Async("asyncTaskExecutor")
public void ceshi() {
log.info("ceshi");
}
}
3、定义一个 http 接口
还可以通过接口的形式来异步调用多线程:
@RestController
@RequestMapping("thread")
public class ListennerTest2 {
@Autowired
private ThreadTest t;
@GetMapping("ceshi2")
public void run() {
for (int i = 1; i < 10; i ) {
t.ceshi2();
}
}
}
@Component
@Slf4j
public class ThreadTest {
@Async("asyncTaskExecutor")
public void ceshi2() {
for (int i = 0; i <= 3; i ) {
log.info("ceshi2");
}
}
}
4、测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class ThreadRunTest {
@Autowired
private ThreadTest t;
@Test
public void thread1() {
for (int i = 1; i <= 10; i ) {
t.ceshi4();
}
}
}
@Component
@Slf4j
public class ThreadTest {
@Async("asyncTaskExecutor")
public void ceshi4() {
log.info("ceshi4");
}
}
四、总结
以上主要介绍了 ThreadPoolTaskExecutor 线程池的配置、使用、相关注解的意义及作用,也简单介绍了使用 @Async 来异步调用线程,最后又列举了多线程的使用场景,并配上了代码示例。这些仅为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
来源:https://blog.csdn.net/CREATE_17/article/details/102540493
猜你喜欢
- 静态变量静态变量位于栈上,它是一个全局变量,在编译期就已经生成。public class Cow{public static int cou
- 一、前言首选,双轴快排也是一种快排的优化方案,在JDK的Arrays.sort()中被主要使用。所以,掌握快排已经不能够满足我们的需求,我们
- 在谈 JVM 内存区域划分之前,我们先来看一下 Java 程序的具体执行过程,我画了一幅图。Java 源代码文件经过编译器编译后生成字节码文
- 本文实例为大家分享了java实现文件上传下载的具体代码,供大家参考,具体内容如下一.上传1.前端:<form method="
- 项目中出了个 BUG,就在我眼皮子底下,很明显的一个 BUG,愣是看了两天才看出来。我有多个任务并发,任务执行完成后都有一个返回结果,我用一
- 一、什么是Java事务通常的观念认为,事务仅与数据库相关。  
- 问题情况:在使用 @TableId(type = IdType.AUTO)之后添加的id数字特别大原因:因为在第一次使用的时候没有加注解 所
- Spring Boot1.为什么要使用 Spring Boot因为Spring, SpringMVC 需要使用的大量的配置文件 (xml文件
- 经常要检测某些IP地址范围段的计算机是否在线。有很多的方法,比如进入到网关的交换机上去查询、使用现成的工具或者编写一个简单的DOS脚本等等,
- 本文实例为大家分享了C#实现截图工具小项目的具体代码,供大家参考,具体内容如下1.起因一直用的截图是qq的截图,所以想要实现一个简单点的截图
- 在生产环境中,需要实时或定期监控服务的可用性。spring-boot 的actuator(监控)功能提供了很多监控所需的接口。简单的配置和使
- 异步log4j2的location信息打印问题背景:项目改造过程中将log4j2改成异步,发现行号没有打印,于是扒了下官方文档,大概陈述下:
- 本文实例讲述了Java正则验证正整数的方法。分享给大家供大家参考,具体如下:package des;import java.util.reg
- 高快省的排序算法有没有既不浪费空间又可以快一点的排序算法呢?那就是“快速排序”啦!光听这个名字是不是就觉得很高端呢。假设我们现在对“6 1
- 浅谈java内存模型 不同的平台,内存模型是不一样的,但是jvm的
- android RecyclerView不像过去的ListView那样随意的设置水平方向的分割线,如果要实现RecyclerView的水平/
- 前文传送门:Netty启动流程服务端channel初始化注册多路复用回到上一小节的代码:final ChannelFuture initAn
- 一、准备java我已经把java装到了在D盘:二、配置java环境变量点击设置,进入windows设置页面;搜索高级系统设置:在系统变量里添
- 本文为大家分享了使用静态关键字实现单例模式的具体代码,供大家参考,具体内容如下单例模式:只能获得某个类的唯一一个实例单例模式,不管什么时间点
- 1.先导入以下两个jar包2.jsp文件表单必须是post提交,编码必须是multipart/form-data 文件上传文本框必须起名。3