Spring Boot使用@Async实现异步调用:自定义线程池

在之前的Spring Boot基础教程系列中,已经通过《Spring Boot中使用@Async实现异步调用》一文介绍过如何使用@Async注解来实现异步调用了。但是,对于这些异步执行的控制是我们保障自身应用健康的基本技能。本文我们就来学习一下,如果通过自定义线程池的方式来控制异步调用的并发。

本文中的例子我们可以在之前的例子基础上修改,也可以创建一个全新的Spring Boot项目来尝试。

定义线程池

第一步,先在Spring Boot主类中定义一个线程池,比如:

1@SpringBootApplication 2public class Application { 3 4 public static void main(String[] args) { 5 SpringApplication.run(Application.class, args); 6 } 7 8 @EnableAsync 9 @Configuration 10 class TaskPoolConfig { 11 12 @Bean("taskExecutor") 13 public Executor taskExecutor() { 14 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 15 executor.setCorePoolSize(10); 16 executor.setMaxPoolSize(20); 17 executor.setQueueCapacity(200); 18 executor.setKeepAliveSeconds(60); 19 executor.setThreadNamePrefix("taskExecutor-"); 20 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 21 return executor; 22 } 23 } 24 25}

上面我们通过使用ThreadPoolTaskExecutor创建了一个线程池,同时设置了以下这些参数:

  • 核心线程数10:线程池创建时候初始化的线程数
  • 最大线程数20:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
  • 缓冲队列200:用来缓冲执行任务的队列
  • 允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
  • 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
  • 线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务

使用线程池

在定义了线程池之后,我们如何让异步调用的执行任务使用这个线程池中的资源来运行呢?方法非常简单,我们只需要在@Async注解中指定线程池名即可,比如:

1@Slf4j 2@Component 3public class Task { 4 5 public static Random random = new Random(); 6 7 @Async("taskExecutor") 8 public void doTaskOne() throws Exception { 9 log.info("开始做任务一"); 10 long start = System.currentTimeMillis(); 11 Thread.sleep(random.nextInt(10000)); 12 long end = System.currentTimeMillis(); 13 log.info("完成任务一,耗时:" + (end - start) + "毫秒"); 14 } 15 16 @Async("taskExecutor") 17 public void doTaskTwo() throws Exception { 18 log.info("开始做任务二"); 19 long start = System.currentTimeMillis(); 20 Thread.sleep(random.nextInt(10000)); 21 long end = System.currentTimeMillis(); 22 log.info("完成任务二,耗时:" + (end - start) + "毫秒"); 23 } 24 25 @Async("taskExecutor") 26 public void doTaskThree() throws Exception { 27 log.info("开始做任务三"); 28 long start = System.currentTimeMillis(); 29 Thread.sleep(random.nextInt(10000)); 30 long end = System.currentTimeMillis(); 31 log.info("完成任务三,耗时:" + (end - start) + "毫秒"); 32 } 33 34}

单元测试

最后,我们来写个单元测试来验证一下

1@RunWith(SpringJUnit4ClassRunner.class) 2@SpringBootTest 3public class ApplicationTests { 4 5 @Autowired 6 private Task task; 7 8 @Test 9 public void test() throws Exception { 10 11 task.doTaskOne(); 12 task.doTaskTwo(); 13 task.doTaskThree(); 14 15 Thread.currentThread().join(); 16 } 17 18}

执行上面的单元测试,我们可以在控制台中看到所有输出的线程名前都是之前我们定义的线程池前缀名开始的,说明我们使用线程池来执行异步任务的试验成功了!

12018-03-27 22:01:15.620 INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task : 开始做任务一 22018-03-27 22:01:15.620 INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task : 开始做任务二 32018-03-27 22:01:15.620 INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task : 开始做任务三 42018-03-27 22:01:18.165 INFO 73703 --- [ taskExecutor-2] com.didispace.async.Task : 完成任务二,耗时:2545毫秒 52018-03-27 22:01:22.149 INFO 73703 --- [ taskExecutor-3] com.didispace.async.Task : 完成任务三,耗时:6529毫秒 62018-03-27 22:01:23.912 INFO 73703 --- [ taskExecutor-1] com.didispace.async.Task : 完成任务一,耗时:8292毫秒

完整示例:

读者可以根据喜好选择下面的两个仓库中查看Chapter4-1-3项目:

如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )