spring方法异步调用async,springboot async不起作用
永远的神干货盘点
背景在开发一个跳羚的项目中,需要批量的快速的插入数据库,于是想到了多线程分配次的插入数据,由于我们项目使用的跳羚项目,查看官网支持线程池
开启异步线程@启用异步
默认线程池threadpooltasktexecutor
查看源码发现自动装配原理
@ condition alon类(threadpooltaskmexecutor。class)@ Configuration(proxy bean methods=false)@ EnableConfigurationProperties(taskexecutionproperties。类)公共类TaskExecutionAutoConfiguration {/* * *应用程序的豆名称{@link TaskExecutor} .*/public静态最终字符串APPLICATION _ TASK _ EXECUTOR _ BEAN _ NAME= applicationtaskmexecutor ;@ Bean @ ConditionalOnMissingBean public TaskExecutorBuilder TaskExecutorBuilder(taskexecutorproperties属性,对象提供者TaskExecutorCustomizer taskExecutorCustomizers,对象提供者task decorator task decorator){ taskexecutorproperties .pool pool=属性。get pool();TaskExecutorBuilder builder=new TaskExecutorBuilder();建设者=建设者。队列容量(池。getqueuecapacity());建设者=建设者。corepoolsize(池。getcoresize());建设者=建设者。maxpoolsize(池。get maxsize());建设者=建设者。allowcorethreadtime out(池。isaallowcorethreadtime out());建设者=建设者。keepalive(池。getkeepalive());关闭关闭=属性。get shut down();建设者=建设者。等待终止(关闭。isawaittermination());建设者=建设者。awaitterminationperiod(关闭。getwaitterminationperiod());建设者=建设者。threadname前缀(属性。getthreadname前缀());建设者=建设者。定制器(taskexecutor定制器。有序流():迭代器);建设者=建设者。任务装饰者(task decorator。getifunique());返回构建器;} @ Lazy @ Bean(NAME={ APPLICATION _ TASK _ EXECUTOR _ Bean _ NAME,AsyncAnnotationBeanPostProcessor .DEFAULT _ TASK _ EXECUTOR _ BEAN _ NAME })@ conditionalomissingbean(EXECUTOR。class)public threadpooltasktexecutor applicationtasktexecutor(TaskExecutorBuilder builder){ return builder。build();}}TaskExecutionProperties主要做配置文件参数映射类
@配置属性( spring。任务。执行’)公共类TaskExecutionProperties { private final Pool Pool=new Pool();对应的性能文件
#核心线程数春天。任务。执行。游泳池。核心尺寸=8 #存活时间春天。任务。执行。游泳池。保活=60s #最大线程数春天。任务。执行。游泳池。max-size=#阻塞队列大小春天。任务。执行。游泳池。队列容量=# spring。任务。执行。线程名称前缀=名称前缀具体可以查看跳羚的参数配置《Springboot参数配置》
自定义线程池
从上面的默认使用的线程池的来看,缺少拒绝策略和其他的一些重要参数,为了满足我们日常的需求我们需要自定义线程吹来满足业务场景
自定义线程池
@配置
@启用异步
@Slf4j
公共类ExecutorConfig {
@ Value( $ { async。执行人。线程。核心池大小} )
private int corePoolSize
@ Value( $ { async。执行人。线程。最大池大小} )
private int maxPoolSize
@ Value( $ { async。执行人。线程。queue _ capacity } )
专用int队列容量
@ Value( $ { async。执行人。线程。姓名。前缀} )
私有字符串名称前缀
@ Bean(name= asyncServiceExecutor )
公共执行程序asyncServiceExecutor() {
日志。info( start asyncServiceExecutor-);
threadpooltasktexecutor=new threadpooltasktexecutor();
//配置核心线程数
执行人。setcorepoolsize(corePoolSize);
//配置最大线程数
执行人。setmaxpoolsize(maxPoolSize);
//配置队列大小
执行人。setqueuecapacity(队列容量);
//配置线程池中的线程的名称前缀
执行人。setthreadname前缀(名称前缀);
//拒绝策略:当泳池已经达到最大尺寸的时候,如何处理新任务
//CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
执行人。setrejectedexecutionhandler(新ThreadPoolExecutor .callerrunpolicy());
//执行初始化
executor.initialize().
日志。info( end asyncServiceExecutor-);
返回执行人;
}
配置文件
# 配置核心线程数
异步:
执行者:
线程:
# 配置核心线程数
核心池大小:5
# 配置最大线程数
最大池大小:5
# 配置队列大小
队列容量:100
# 配置线程池中的线程的名称前缀
名称:
前缀:异步服务-
测试验证
异步服务
@Async
@Slf4j
@服务
公共类异步服务{
* 指定线程池,当有多个线程池,需要指定名字
* @次投掷中断异常
@Async(asyncServiceExecutor )
public void executeAsync(整数)引发中断的异常{
日志。info(“start execute async”);
线程。睡眠(2000年);
System.out.println(异步线程执行. num);
日志。info(“end execute async”);
}
控制器
@自动连线
AsyncServiceImpl异步服务
@GetMapping
公共字符串测试异步(@ request param( num )int num)引发中断的异常{
for(int I=0;i numi ) {
异步服务。执行异步(I);
返回"确定";
}
通过邮递员测试结果如下,通过结果可以看到,我们一共请求了3次,每个任务执行的时间为2秒,但是接口会立刻返回数据好到客户端,实现了多线程异步处理的功能
22:30:08.059[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[execute async,21] - start executeAsync
22:30:08.059[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[execute async,21] - start executeAsync
22:30:08.059[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[execute async,21] - start executeAsync
异步线程执行.一
异步线程执行.0
异步线程执行.2
22:30:10.072[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[execute async,24] - end executeAsync
22:30:10.072[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[execute async,24] - end executeAsync
22:30:10.072[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[execute async,24] - end executeAsync
自定义可视化线程池
虽然我们已经用上了线程池,但是还不清楚线程池当时的情况,有多少线程在执行,多少在队列中等待呢?这里我创建了一个threadpooltasktexecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来
@Slf4j
公共类visiablethreadpooltasktexecutor扩展了ThreadPoolTaskExecutor {
私有void showThreadPoolInfo(字符串前缀){
ThreadPoolExecutor ThreadPoolExecutor=getThreadPoolExecutor();
if (null==threadPoolExecutor) {
返回;
log.info({},{},taskCount [{}],completedTaskCount [{}],activeCount [{}],queueSize [{}],
this.getThreadNamePrefix(),
前缀,
threadpoolexecutor。gettaskcount(),
threadpoolexecutor。getcompletedtaskcount(),
threadpoolexecutor。getactivecount(),
threadPoolExecutor.getQueue().size());
@覆盖
公共void execute(可运行任务){
showThreadPoolInfo(1 .do execute’);
超级执行(任务);
@覆盖
public void execute(Runnable task,long startTimeout) {
showThreadPoolInfo(2 .do execute’);
super.execute(任务,开始超时);
@覆盖
大众未来?提交(可运行的任务){
showThreadPoolInfo(1 .务必提交);
返回超级。提交(任务);
@覆盖
公共测试未来测试提交(可调用测试任务){
showThreadPoolInfo(2 .务必提交);
返回超级。提交(任务);
@覆盖
公众ListenableFuture?提交列表使能(可运行的任务){
showThreadPoolInfo(1 .do submitlistanable’);
返回超级棒。submitlistanable(任务);
@覆盖
公共测试列表启用未来测试提交列表启用(可调用测试任务){
showThreadPoolInfo(2 .do submitlistanable’);
返回超级棒。submitlistanable(任务);
}
替换默认的线程池
@ Bean(name= asyncServiceExecutor )
公共执行程序asyncServiceExecutor() {
日志。info( start asyncServiceExecutor-);
//threadpooltasktexecutor=new threadpooltasktexecutor();
//使用可视化运行状态的线程池
threadpooltasktexecutor=new visiablethreadpooltasktexecutor();
//配置核心线程数
执行人。setcorepoolsize(corePoolSize);
//配置最大线程数
执行人。setmaxpoolsize(maxPoolSize);
//配置队列大小
执行人。setqueuecapacity(队列容量);
//配置线程池中的线程的名称前缀
执行人。setthreadname前缀(名称前缀);
//拒绝策略:当泳池已经达到最大尺寸的时候,如何处理新任务
//CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
执行人。setrejectedexecutionhandler(新ThreadPoolExecutor .callerrunpolicy());
//执行初始化
executor.initialize().
日志。info( end asyncServiceExecutor-);
返回执行人;
}
这说明提交任务到线程池的时候,调用的是提交(可调用任务)这个方法,当前已经提交了2个任务,完成了0个,当前有2个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然;
异步服务,2。做提交,任务计数[2],已完成任务计数[0],活动计数[2],队列大小[0]
返回值的接口
我们经常会使用线程池的返回值,可以使用未来测试或者CompletableFuture T接收
异步服务
/**
* 指定线程池,当有多个线程池,需要指定名字
* 带有返回值
* @次投掷中断异常
@Async(asyncServiceExecutor )
public completable future String executevaluasync()引发中断的异常{
日志。信息( start executevaluasync );
线程。睡眠(200);
System.out.println(异步线程执行返回结果. );
日志。信息( end executevaluasync );
返回completablefuture。已完成的未来(你好);
}
控制器
@GetMapping(/result )
公共字符串testVlaueAsync(@ request param( num )int num)抛出InterruptedException,ExecutionException {
字符串结果="";
list completable future list=new ArrayList();
for(int I=0;i numi ) {
可完成的未来字符串CompletableFuture=异步服务。executevaluasync();
列表。add(completableFuture);
for(CompletableFuture CompletableFuture:list){
结果=结果completablefuture。get();
返回结果;
}
测试结果返回正常的数据
22:53:42.443[http-nio-8081-exec-2]INFO c . r . r . c . visiablethreadpooltasktexecutor-[showThreadPoolInfo,23] - async-service-,1 .do execute,taskCount [0],completedTaskCount [0],activeCount [0],queueSize [0]
22:53:42.447[http-nio-8081-exec-2]INFO c . r . r . c . visiablethreadpooltasktexecutor-[showThreadPoolInfo,23] - async-service-,1 .do execute,taskCount [1],completedTaskCount [0],activeCount [1],queueSize [0]
22:53:42.448[http-nio-8081-exec-2]INFO c . r . r . c . visiablethreadpooltasktexecutor-[showThreadPoolInfo,23] - async-service-,1 .执行,任务计数[2],完成任务计数[0],活动计数[2],队列大小[0]
22:53:42.449[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,34]-start executevaluesync
22:53:42.449[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,34]-start executevaluesync
22:53:42.449[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,34]-start executevaluesync
异步线程执行返回结果.
异步线程执行返回结果.
异步线程执行返回结果.
22:53:42.662[async-service-3]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,37]-end executevaluesync
22:53:42.662[async-service-2]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,37]-end executevaluesync
22:53:42.662[async-service-1]INFO c . r . r . s . I . asyncserviceimpl-[executevaluesync,37]-end executevaluesync
22:53:42.663[http-nio-8081-exec-2]INFO c . r . r . c . test 2控制器-[testVlaueAsync,44] -结果:你好你好你好
注意
使用异步线程返回类型有2中空的和未来测试,其他情况都会报错
本文主要从默认线程池,自定义线程池,可视化线程池三个角度分析了跳羚@异步实现异步线程。
小棋子目前开放微信公众号,搜索"霸都学java "大家可以关注持续输出原创文章,欢迎关注一波
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。