springboot @Async 注解如何实现方法异步
作者:Bug布道师 发布时间:2023-11-18 18:16:03
@Async注解如何实现方法异步
处理大批量数据的时候,效率很慢。所以考虑一下使用多线程。
刚开始自己手写的一套,用了线程池启动固定的线程数进行跑批。但是后来老大考虑到自己手写的风险不好控制,所以使用spring的方法。
这里没有详细介绍,只有简单的demo,只会用,不懂原理:
一、springboot的App类需要的注解
package com.xxx.xxx.xxx;
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* 类功能说明:服务生产者启动类
* <p>
* <strong></strong>
* </p>
*
* @version
* @author
* @since 1.8
*/
@Configuration
@EnableAsync
public class Application extends SpringBootServletInitializer {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(5);
// 设置最大线程数
executor.setMaxPoolSize(60);
// 设置队列容量
executor.setQueueCapacity(20);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("what-");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
springboot的App类,很简单,就能使用很多东西。
二、service层的注解
package com.xxx.xxx.service.impl;
import java.util.concurrent.Future;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import com.xxx.xxx.service.XXXAsyncService ;
@Service
public class XXXAsyncServiceImpl implements XXXAsyncService {
@Async
public Future<Long> rtn1() throws Exception {
//do something
//有返回值的时候,可以返回string,long之类的。
return new AsyncResult<>(1);
}
@Async
public void rtn2() throws Exception {
//do something
//这个可以没有返回值.
}
}
三、调用层
package com.xxx.xxx.controller;
import java.util.concurrent.Future;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.xxx.xxx.service.XXXAsyncService;
@RestController
@RequestMapping(value="/xxx")
public class XXXAsyncController {
@Autowired
private XXXAsyncService xxxAsyncService;
/**
* 这里调用异步方法
*/
@RequestMapping(value = "/xxx")
public void dodo() throws Exception {
int threads = 10;//十个线程
List<Future<Long>> list = new ArrayList<>();
for(int i = 0;i < threads; i++){
//这里循环调用异步方法。
//如果存在大量数据,可以在这里把数据切片,然后循环调用,分批处理数据。效率杠杠的。
list .add(xxxAsyncService.rtn1());
}
long count = 0;
for(Future<Long> l : tsfCountList) {
//异步调用需要返回值的时候,这里可以把返回值都放入到list集合中,然后可以统一处理。 这里的get()是阻塞的,因为需要所以异步方法返回,在继续执行。
count += l.get();
}
System.out.println("调用次数:" + count);
}
}
这些代码全是手写,记录下来,以后用的时候,省的忘了,查起来麻烦。。
异步注解@Async的使用以及注意事项
第一步开启异步
@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }
默认情况下,@EnableAsync检测Spring的@Async注释和EJB 3.1 javax. EJB .异步;此选项还可用于检测其他用户定义的注释类型。(也可以在SpringBoot的启动类上直接加@EnableAsync注解)
在 Spring 中,用 @Async 注解指定的方法,该方法被调用时会以异步的方式执行。而如果没有在 @Async 注解中指定线程池,就会使用默认的线程池。默认的线程池为 SimpleAsyncTaskExecutor 。
该线程池不会复用线程,每有一个新任务被提交,该线程池就会创建一个新的线程实例用于执行任务。下面为相关的代码:
protected void doExecute(Runnable task) {
Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
thread.start();
}
而如果想要指定线程池,可以通过在 @Async 注解中的 value 参数中指定所要使用的线程池的 Bean Name 。另一种方法是是一个实现了 AsyncConfigurer 接口或是继承其默认适配器类 AsyncConfigurerSupport 的配置类,这样 @Async 注解的方法就会使用指定的自定义的线程池。
使用@Async注解的话采用的是springBoot默认的线程池,不过一般我们会自定义线程池(因为比较灵活),配置方式有:
使用 xml 文件配置的方式
使用Java代码结合@Configuration进行配置(推荐使用)
下面显示配置线程的代码实现
package com.deppon.ptos.load.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
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.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Description: 异步线程管理
* @Author: LYH
* @CreateDate: 2019/6/27 8:54
* @Version: 1.0
* @JDK: 1.8
*/
@Configuration
@EnableAsync
@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;
@Bean(name = "asyncServiceExecutor")
public Executor asyncServiceExecutor() {
log.info("start asyncServiceExecutor");
ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(corePoolSize);
//配置最大线程数
executor.setMaxPoolSize(maxPoolSize);
//配置队列大小
executor.setQueueCapacity(queueCapacity);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(namePrefix);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
executor.initialize();
return executor;
}
}
package com.deppon.ptos.load.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.concurrent.ListenableFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Description: 打印异步线程的执行情况 使用Callbale Future 来返回线程的信息
* @Author: 633805 LYH
* @CreateDate: 2019/6/27 8:59
* @Version: 1.0
* @JDK: 1.8
*/
@Component
@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
return;
}
log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1. do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2. do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1. do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2. do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1. do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2. do submitListenable");
return super.submitListenable(task);
}
}
使用:
@Async("asyncServiceExecutor")
到这一步,异步就算开启了。
下面主要说一说错误的
使用@Async导致异步不成功的情况
如下方式会使@Async失效
异步方法使用static修饰
异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
异步方法不能与被调用的异步方法在同一个类中
类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
来源:https://blog.csdn.net/u013100581/article/details/82657877
猜你喜欢
- Java为什么不浪(long)学而时习之不亦说乎,继续温习Java。今天使用switch时,不小心写了如下代码,报错如下。 public s
- 一. 为什么要使用接口假如有一个需求:要求实现防盗门的功能。门有"开"和"关"的功能,锁有"
- 在Java中如果一个类同时继承接口A与B,并且这两个接口中具有同名方法,会怎么样?动手做实验:interface A{ void
- 需求是需要在TextView前端加入一个标签展示。最终效果图如下:根据效果图,很容易就能想到使用SpannableStringBuilder
- fopen(打开文件)相关函数 open,fclose表头文件 #include<stdio.h>定义函数 FILE * fop
- java有两种类型的classload,一种是user-defined的,一种是jvm内置的bootstrap class loader,所
- spring mvc中的@PathVariable是用来获得请求url中的动态参数的,十分方便,复习下: @Controller publ
- 打包与运行在项目开发完成之后,可以直接用IDEA将其打包成JAR包运行,也可以打包成WAR包运行以便在多服务器、多配置环境下运行。双击cle
- 1、什么是servlet异步请求Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:(1)、Servlet 接收
- 定义与结构 备忘录(Memento)模式又称标记(Token)模式。GOF给备忘录模式的定义为:在不破坏
- 0 实验环境在Android Studio中进行有关代码的编写和界面效果展示。SQLite数据库的图形化工具SQLiteStudio下载网址
- Handler每个初学Android开发的都绕不开Handler这个“坎”,为什么说是个坎呢,首先这是Android架构的精髓之一,其次大部
- 1.需求背景需要实现一个动态加载但不显示出来的视图,且该视图上有个动态生成的二维码,最后用其去生成一张快照(也就是图片)。(常见这种情况是来
- 一、封装类1.封装类概念Java中存在基础数据类型,但是在某些情况下,我们要对基础数据类型进行对象的操作,例如,集合中只能存对象,而不能存在
- 最近没事写了一个简易浏览器,在刚开始写的时候遇到一些问题,主要的问题就是如何在自己的webview中显示所有的网页数据,不过不指
- 本文实例为大家分享了Android实现双曲线折线图的具体代码,供大家参考,具体内容如下先看一下效果图1.先下载jar包 mpandroidc
- 1. matlab的lp2lp函数的作用去归一化 H(s) 的分母2. matlab的lp2lp函数的使用方法[z, p, k]=butta
- Java调用接口获取json数据保存到数据库今天给大家带来一个调用接口,来获取数据解析后再保存到数据库中的业务,业务中的Mapper和实体类
- 报错翻译: compileSdkVersion android-24”需要JDK 1.8或更高版本编译。报错现象如下图:原因:st
- 年纪大了,以前做过的东西过阵子还是会忘,今天使用jenkins持续集成工具时用到了eclipse上传新maven工程至svn,上传完毕后改了