因业务需要,经常会遇到主线程中包含其他关联业务,然关联业务的执行结果对主线程的返回结果没有直接影响或无影响。此时,能让主线程更顺畅的执行,并给客户带来好的客户体验,我们一般会将该关联业务做异步处理或类似的处理(如:消息队列)。然后Spring为我们提供了开启异步任务的方法。
一、Springboot 使用异步任务
1、SpringBootApplication启动类添加@EnableAsync注解;
2、@Async使用
(1)类或者方法中使用@Async注解,类上标有该注解表示类中方法都是异步方法,方法上标有该注解表示方法是异步方法;
(2)@Async(“threadPool”),threadPool为自定义线程池,这样可以保证主线程中调用多个异步任务时能更高效的执行。
3、实例、分析
开启异步服务
package com.llq.controller;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class RedisApplication {
public static void main(String[] args) {
SpringApplication.run(RedisApplication.class,args);
}
}
创建线程池
package com.llq.controller.main;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class AsyncService {
private static final int core = 10;//核心线程数
private static final int queueNum = 10;//队列容量
private static final int max = 50;//最大线程数
@Bean(name = "executeThread")
public Executor executeThread() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(core);
taskExecutor.setMaxPoolSize(max);
taskExecutor.setQueueCapacity(queueNum);
taskExecutor.setRejectedExecutionHandler(new MyThreadRejectedHandler());//线程池拒绝策略
taskExecutor.initialize();
return taskExecutor;
}
}
自定义拒绝策略
package com.llq.controller.main;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
public class MyThreadRejectedHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
}
}
执行异步调用
package com.llq.controller.main;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Component
public class ExecuteAsyncMethod {
private int num = 10000;
@Async(value = "executeThread")
public Future<String> execute(){
for (int i = 0; i < 100; i++) {
num--;
}
return new Future<String>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return false;
}
@Override
public String get() throws InterruptedException, ExecutionException {
return ""+num;
}
@Override
public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return null;
}
};
}
@Async(value = "executeThread")
public String execute2(){
for (int i = 0; i < 100; i++) {
num--;
}
return ""+num;
}
}
注意: 如果想要在异步调用方法后有返回值,那么需要使用Future做返回值,否则调用方返回数据为null
如上execute
和execute2
方法。
package com.llq.controller.main;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@RestController
public class MyController {
@Autowired
private ExecuteAsyncMethod executeAsyncMethod;
@RequestMapping("/test")
public String execute() throws ExecutionException, InterruptedException {
Future<String> execute = executeAsyncMethod.execute();
System.out.println(execute.get());
String s1 = executeAsyncMethod.execute2();
System.out.println(s1);
return " --- " + s1;
}
}
至此,Springboot 调用多个异步任务使用实例完成。
@Async注解 分析
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
String value() default "";
}
通过查看@Async源码可以看出来,@Target 的值是类和方法,所有异步注解可以作用在方法体上面,也可以作用在类上面。
以上是项目使用,可正常使用,因此验证忽略!
二、Spring boot异步任务@Async失效问题
总结:
1、启动类是否开启异步服务;
2、在定义异步方法的同一个类中,调用带有@Async注解方法,该方法则无法异步执行;
3、注解的方法必须是public方法,不能是static;
4、没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器管理。
参考:https://blog.csdn.net/d980206432/article/details/114366343