文章目录

  • 概述
  • V1.0 默认的实现
    • Step1 搞配置类,开启@EnableAsync
    • Step2 搞方法标记 @Async注解
    • Step3 搞调用
  • Spring提供的线程池
  • V2.0 实现@Async的自定义线程池
  • V3.0 多个线程池处理
    • 多个线程池
    • 默认线程池
    • 验证一把
  • 源码


概述

Spring3开始提供了@Async注解,我们只需要在方法上标注此注解,此方法即可实现异步调用。 除此之外, 还得需要一个配置类,通过@EnableAsync 来开启异步功能 。


V1.0 默认的实现

Step1 搞配置类,开启@EnableAsync

我们需要使用@EnableAsync来开启异步任务支持。

@EnableAsync注解可以直接放在SpringBoot启动类上,也可以单独放在其他配置类上。

我们这里选择单独搞个配置类

@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {}


Step2 搞方法标记 @Async注解

package com.artisan.jobs;import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2022/3/1 0:42* @mark: show me the code , change the world*/@Component
@Slf4j
public class AsyncJob {@Async public void job1() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job1 cost {} ms", endTime - beginTime);}@Async public void job2() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job2 cost {} ms", endTime - beginTime);}
}


Step3 搞调用

package com.artisan.controller;import com.artisan.jobs.AsyncJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2022/3/1 0:44* @mark: show me the code , change the world*/@RestController
@RequestMapping("/async")
@Slf4j
public class AsyncController {@Autowiredprivate AsyncJob asyncJob;@RequestMapping("/job")public String task() throws InterruptedException {long beginTime = System.currentTimeMillis();// 执行异步任务asyncJob.job1();asyncJob.job2();// 模拟业务耗时Thread.sleep(1000);long cost = System.currentTimeMillis() - beginTime;log.info("main cost {} ms", cost);return "Task Cost " + cost + " ms";}
}

@Async注解在默认情况下用的是SimpleAsyncTaskExecutor线,不是真正意义上的线程池。



所以,线程名称是 task-1 , task-2, task-3 , task-4…

2022-03-02 22:33:47.007 [http-nio-8080-exec-6] INFO  com.artisan.controller.AsyncController:39 - main cost 1001 ms
2022-03-02 22:33:47.675 [http-nio-8080-exec-2] INFO  com.artisan.controller.AsyncController:39 - main cost 1001 ms
2022-03-02 22:33:48.021 [task-4] INFO  com.artisan.jobs.AsyncJob:35 - job2 cost 2014 ms
2022-03-02 22:33:48.021 [task-3] INFO  com.artisan.jobs.AsyncJob:26 - job1 cost 2014 ms
2022-03-02 22:33:48.396 [http-nio-8080-exec-5] INFO  com.artisan.controller.AsyncController:39 - main cost 1015 ms
2022-03-02 22:33:48.678 [task-6] INFO  com.artisan.jobs.AsyncJob:35 - job2 cost 2004 ms
2022-03-02 22:33:48.678 [task-5] INFO  com.artisan.jobs.AsyncJob:26 - job1 cost 2004 ms
2022-03-02 22:33:49.004 [http-nio-8080-exec-3] INFO  com.artisan.controller.AsyncController:39 - main cost 1008 ms
2022-03-02 22:33:49.393 [task-8] INFO  com.artisan.jobs.AsyncJob:35 - job2 cost 2011 ms
2022-03-02 22:33:49.393 [task-7] INFO  com.artisan.jobs.AsyncJob:26 - job1 cost 2011 ms
2022-03-02 22:33:50.012 [task-9] INFO  com.artisan.jobs.AsyncJob:26 - job1 cost 2015 ms
2022-03-02 22:33:50.012 [task-10] INFO  com.artisan.jobs.AsyncJob:35 - job2 cost 2015 ms

可以看到,每次调用都会new一个线程。若系统中不断的创建线程…


Spring提供的线程池

名称 说明
SimpleAsyncTaskExecutor 这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地
ConcurrentTaskExecutor Executor的适配类,不推荐使用。如ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类
ThreadPoolTaskScheduler 可以使用cron表达式
ThreadPoolTaskExecutor 推荐。 是对java.util.concurrent.ThreadPoolExecutor的包装


V2.0 实现@Async的自定义线程池

package com.artisan.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;/*** @author 小工匠* @version 1.0* @description: 使用@EnableAsync来开启异步任务支持,@EnableAsync注解可以直接放在SpringBoot启动类上,也可以单独放在其他配置类上。* 我们这里选择使用单独的配置类AsyncConfiguration。* @date 2022/3/1 0:41* @mark: show me the code , change the world*/@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {/*** 核心线程数(默认线程数)*/private static final int CORE_POOL_SIZE = 5;/*** 最大线程数*/private static final int MAX_POOL_SIZE = 10;/*** 允许线程空闲时间(单位:默认为秒)*/private static final int KEEP_ALIVE_TIME = 10;/*** 缓冲队列大小*/private static final int QUEUE_CAPACITY = 200;/*** 线程池名前缀*/private static final String THREAD_NAME_PREFIX = "Async-Service-";/*** 自定义线程池** @return*/@Bean("customAsyncPoolTaskExecutor")public ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CORE_POOL_SIZE);executor.setMaxPoolSize(MAX_POOL_SIZE);executor.setQueueCapacity(KEEP_ALIVE_TIME);executor.setKeepAliveSeconds(QUEUE_CAPACITY);executor.setThreadNamePrefix(THREAD_NAME_PREFIX);/*** 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略* 通常有以下四种策略:* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。* ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}}

其他保持不变, 重启测试


V3.0 多个线程池处理

需求: 不同的业务,使用不同的线程池

多个线程池

package com.artisan.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;/*** @author 小工匠* @version 1.0* @description: 使用@EnableAsync来开启异步任务支持,@EnableAsync注解可以直接放在SpringBoot启动类上,也可以单独放在其他配置类上。* 我们这里选择使用单独的配置类ThreadPoolTaskConfig* @date 2022/3/1 0:41* @mark: show me the code , change the world*/@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {/*** 核心线程数(默认线程数)*/private static final int CORE_POOL_SIZE = 5;/*** 最大线程数*/private static final int MAX_POOL_SIZE = 10;/*** 允许线程空闲时间(单位:默认为秒)*/private static final int KEEP_ALIVE_TIME = 10;/*** 缓冲队列大小*/private static final int QUEUE_CAPACITY = 200;/*** 线程池名前缀*/private static final String THREAD_NAME_PREFIX = "Biz1_Async-Service-";/*** 线程池名前缀*/private static final String THREAD_NAME_PREFIX_2= "Biz2_Async-Service-";/*** 自定义线程池** @return*/@Bean("tp1")public ThreadPoolTaskExecutor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CORE_POOL_SIZE);executor.setMaxPoolSize(MAX_POOL_SIZE);executor.setQueueCapacity(KEEP_ALIVE_TIME);executor.setKeepAliveSeconds(QUEUE_CAPACITY);executor.setThreadNamePrefix(THREAD_NAME_PREFIX);/*** 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略* 通常有以下四种策略:* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。* ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}/*** 自定义线程池** @return*/@Bean("tp2")public ThreadPoolTaskExecutor taskExecutor2() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(CORE_POOL_SIZE);executor.setMaxPoolSize(MAX_POOL_SIZE);executor.setQueueCapacity(KEEP_ALIVE_TIME);executor.setKeepAliveSeconds(QUEUE_CAPACITY);executor.setThreadNamePrefix(THREAD_NAME_PREFIX_2);/*** 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略* 通常有以下四种策略:* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。* ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功*/executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());executor.initialize();return executor;}}

配置 多个线程池, 然后 为@Async指定线程池名字即可实现 多个线程池处理

package com.artisan.jobs;import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2022/3/1 0:42* @mark: show me the code , change the world*/@Component
@Slf4j
public class AsyncJob {@Async("tp1")public void job1() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job1 cost {} ms", endTime - beginTime);}@Async("tp2")public void job2() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job2 cost {} ms", endTime - beginTime);}@Async()public void job3() throws InterruptedException {long beginTime = System.currentTimeMillis();Thread.sleep(2000);long endTime = System.currentTimeMillis();log.info("job3 cost {} ms", endTime - beginTime);}
}

默认线程池

@Async()没标注,用哪个?????? 当系统存在多个线程池时,我们也可以配置一个默认线程池 ,配置类让其实现AsyncConfigurer,并重写getAsyncExecutor()方法,指定默认线程池

package com.artisan.multi;import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;/*** @author 小工匠* @version 1.0* @mark: show me the code , change the world* <p>* 实现AsyncConfigurer,并重写getAsyncExecutor()方法,指定默认线程池*/@Configuration
@EnableAsync
@Slf4j
public class DefaultAsyncConfiguration implements AsyncConfigurer {/*** 核心线程数(默认线程数)*/private static final int CORE_POOL_SIZE = 2;/*** 最大线程数*/private static final int MAX_POOL_SIZE = 10;/*** 允许线程空闲时间(单位:默认为秒)*/private static final int KEEP_ALIVE_TIME = 10;/*** 缓冲队列大小*/private static final int QUEUE_CAPACITY = 200;/*** 线程池名前缀*/private static final String THREAD_NAME_PREFIX = "Default_Async-Service-";@Bean(name = "defaultPool")public ThreadPoolTaskExecutor executor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(CORE_POOL_SIZE);taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);taskExecutor.setQueueCapacity(KEEP_ALIVE_TIME);taskExecutor.setKeepAliveSeconds(QUEUE_CAPACITY);taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX);/*** 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略* 通常有以下四种策略:* ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。* ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。* ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)* ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功*/taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());taskExecutor.initialize();return taskExecutor;}/*** 指定默认线程池*/@Overridepublic Executor getAsyncExecutor() {return executor();}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return (ex, method, params) -> log.error("线程池执行任务发横未知错误,执行方法:{}", method.getName(), ex);}}

验证一把

@RestController
@RequestMapping("/async")
@Slf4j
public class AsyncController {@Autowiredprivate AsyncJob asyncJob;@RequestMapping("/job")public String task() throws InterruptedException {long beginTime = System.currentTimeMillis();// 执行异步任务asyncJob.job1();asyncJob.job2();asyncJob.job3();// 模拟业务耗时Thread.sleep(1000);long cost = System.currentTimeMillis() - beginTime;log.info("main cost {} ms", cost);return "Task Cost " + cost + " ms";}
}

完美匹配…


源码

https://github.com/yangshangwei/boot2

SpringBoot - 优雅的实现【异步编程】相关推荐

  1. springboot异步和切面_Spring异步编程 你的@Async就真的异步吗?异步历险奇遇记

    引言有点长 前端的宝宝会用ajax,用异步编程到快乐的不行~ 我们java也有异步,用起来比他们还快乐~ 我们biaji一个注(gǒupí)解(gāoyào),也是快乐风男... 且看下面的栗子: 注 ...

  2. springboot异步和切面_Spring异步编程 | 你的@Async就真的异步吗 ☞ 异步历险奇遇记...

    引言有点长 前端的宝宝会用ajax,用异步编程到快乐的不行~ 我们java也有异步,用起来比他们还快乐~ 我们bia~ji~一个注(gǒupí)解(gāoyào),也是快乐风男... 且看下面的栗子: ...

  3. springboot异步和切面_Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记

    Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记 点击上方"java进阶架构师",选择右上角"置顶公众号" 20大进阶架构专题每日送达 引 ...

  4. 新手也能看懂的 SpringBoot 异步编程指南

    通过本文你可以了解到下面这些知识点: Future 模式介绍以及核心思想 核心线程数.最大线程数的区别,队列容量代表什么: ThreadPoolTaskExecutor饱和策略: SpringBoot ...

  5. SpringBoot 如何异步编程,老鸟们都这么玩的

    大家好,我是飘渺.今天继续给大家带来SpringBoot老鸟系列的第六篇,来聊聊在SpringBoot项目中如何实现异步编程. 老鸟系列文章导读: 1. SpringBoot 如何统一后端返回格式?老 ...

  6. 笑了,面试官问我知不知道异步编程的Future。

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 荒腔走板 老规矩,先来一个简短的荒腔走板,给冰冷的技术文注 ...

  7. Spring Boot实战:异步编程指南

    通过本文你可以了解到下面这些知识点: Future 模式介绍以及核心思想 核心线程数.最大线程数的区别,队列容量代表什么: ThreadPoolTaskExecutor 饱和策略: SpringBoo ...

  8. java 如何只暴露接口_Java并发异步编程,原来十个接口的活现在只需要一个接口就搞定...

    什么?对你没有听错,也没有看错 ..多线程并发执行任务,取结果归集~~ 不再忧愁-. 引言 先来看一些APP的获取数据,诸如此类,一个页面获取N多个,多达10个左右的一个用户行为数据,比如:点赞数,发 ...

  9. SpringBoot优雅编码之:Lombok加持

    概述 Lombok 通过提供简单的语法注解形式来帮助简化消除一些必须有但显得很臃肿的 java 代码.典型的是对于 POJO对象的简化(如自动帮我们生成Setter和Getter等),有了Lombok ...

  10. 从根本上了解异步编程体系

    作者:ronaldoliu,腾讯 IEG 后台开发工程师 或许你也听说了,摩尔定律失效了.技术的发展不会永远是指数上升,当芯片的集成度越来越高,高到 1 平方毫米能集成几亿个晶体管时,也就是人们常说的 ...

最新文章

  1. chrome浏览器上传文件延迟_扫描识别工具Dynamic Web TWAIN使用教程:移动浏览器捕获(下)...
  2. python括号匹配算法_使用Python的栈实现括号匹配算法
  3. ios 打开评论界面
  4. 安装部署Exchange Server 2010 CAS NLB MailBox DAG
  5. 【C语言进阶深度学习记录】十三 C语言中 ++和--操作符
  6. WINCE下I/O操作基础
  7. Windows11安全中心打不开怎么办 Win11打不开安全中心解决方法
  8. 【BZOJ2095】【POI2010】Bridge 网络流
  9. Oracle DBA必须学会的11个Linux基本命令
  10. Python、Go、JavaScript、Rust 将长盛 5 年!
  11. jQuery基础集锦——插件开发
  12. 分享16款Java小游戏源码Java applet小游戏源码
  13. 关于VB访问数据库的一些经验(献给VB初学者)
  14. 汇编语言学习:如何理解“物理地址=段地址X16+偏移地址”
  15. 【参赛作品101】充实openGauss每日一练21天学习完成大总结
  16. 网络共享显示网络设备没有其他计算机,Win10如何寻找同一网络下的其他设备 网络发现功能无法使用怎么办...
  17. 【甄选靶场】Vulnhub百个项目渗透——项目十:stapler-1(文件上传,多方式提权)
  18. Snort的TILE64移植
  19. 【英语流利说】让你发音更标准的十个窍门
  20. 十行代码让你的单机“影分身”,分布式训练速度快到飞起

热门文章

  1. linux 内存 shared,Linux Shared Memory的查看与设置
  2. 哪里有计算机一级的题库,计算机一级题库带答案哪有?
  3. python executemany
  4. tf.reverse_sequence
  5. ubuntu 循环登录问题,
  6. 在有序旋转数组中找到最小值
  7. LightGBM安装与模型训练
  8. R语言实战应用精讲50篇(七)-因子
  9. 强化学习(八)价值函数的近似表示与Deep Q-Learning
  10. 原理图连线有错误提醒_拔罐方法不对=缩短生命,中医提醒,拔火罐警惕三个禁忌...