Spring @Async 注解的使用
Spring中用@Async
注解标记的方法,称为异步方法,它会在调用方的当前线程之外的独立的线程中执行。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
Spring 已经实现的线程池
1、SimpleAsyncTaskExecutor
:默认情况下每次调用都会创建一个新的线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发 OutOfMemoryError
错误。针对线程创建问题,SimpleAsyncTaskExecutor
提供了限流机制,通过 concurrencyLimit
属性来控制开关,当concurrencyLimit>=0
时开启限流机制,默认关闭限流机制即 concurrencyLimit = -1
,当关闭情况下,会不断创建新的线程来处理任务。默认配置 SimpleAsyncTaskExecutor
并不是严格意义的线程池,达不到线程复用的功能。
2、SyncTaskExecutor
:没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方。
3、ConcurrentTaskExecutor
:Executor
的适配类,不推荐使用。如果 ThreadPoolTaskExecutor
不满足要求时,才考虑使用这个类。
4、 SimpleThreadPoolTaskExecutor
:是 Quartz
的 SimpleThreadPool
的类。线程池同时被 quartz
和非 quartz
使用,才需要使用此类。
5、ThreadPoolTaskExecutor
:推荐使用,其实质是对 java.util.concurrent.ThreadPoolExecutor
的包装。
@Async注解使用条件
1、@Async
注解一般使用在方法上,如果使用在类上,这个类中所有方法都是异步执行的,如果方法上也有该注解,将覆盖类上的注解内容;
2、@Async
注解使用在方法上,方法必须是 public
修饰的,且不能是 static
修饰的;
3、@Async
注解使用在方法上,参数可以是任意类型的,但是方法的返回值必须是 void
或者Future
类型;
4、默认情况下(即@EnableAsync
注解的mode=AdviceMode.PROXY
),同一个类内部没有使用@Async
注解修饰的方法调用@Async
注解修饰的方法,是不会异步执行的。如果想实现类内部自调用也可以异步,则需要切换@EnableAsync
注解的mode=AdviceMode.ASPECTJ
;
5、@Async
注解是基于动态代理实现的;
6、@Async
注解默认使用 SimpleAsyncTaskExecutor
作为线程池,推荐自定义ThreadPoolTaskExecutor
。
@Async注解的使用
1、注册为bean组件
package com.jidi.springbootredis.config;
import com.jidi.springbootredis.exception.AsyncExceptionHandler;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @Description @Async注解使用配置类
* @Author jidi
* @Email jidi_jidi@163.com
* @Date 2021/8/1
*/
@Configuration
@EnableAsync
public class AsyncConfig extends AsyncConfigurerSupport {=
/**
* 自定义线程池
*/
@Bean("executor")
public Executor executor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 线程池核心线程数量
executor.setCorePoolSize(5);
// 队列最大长度
executor.setQueueCapacity(100);
// 最大线程数量
executor.setMaxPoolSize(5);
// 线程池拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 线程名前缀
executor.setThreadNamePrefix("async-");
// 允许空闲时间
executor.setKeepAliveSeconds(30);
// 初始化
executor.initialize();
return executor;
}
/**
* 如果有多个线程池,指定要使用的线程池
*/
@Override
public Executor getAsyncExecutor() {
return executor();
}
/**
* 指定异常处理
*/
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(){
return new AsyncExceptionHandler();
}
}
2、自定义异常处理
package com.jidi.springbootredis.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;
import java.lang.reflect.Method;
/**
* @Description @Async注解使用 自定义异常处理
* @Author jidi
* @Email jidi_jidi@163.com
* @Date 2021/8/1
*/
@Slf4j
public class AsyncExceptionHandler extends SimpleAsyncUncaughtExceptionHandler {
/**
* 自定义异常处理逻辑
*/
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... params) {
StringBuilder info = new StringBuilder();
String msg = throwable.getMessage() != null ? throwable.getMessage() : throwable.getClass().getSimpleName();
info.append("出现异常:").append(msg).append(" methodName: " + method).append("\n");
for (StackTraceElement stackTrace : throwable.getStackTrace()) {
info.append(stackTrace.toString()).append("\n");
}
log.error(info.toString());
}
}
3、定义异步方法
package com.jidi.springbootredis.component;
import com.jidi.springbootredis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.concurrent.Future;
/**
* @Description 异步方法
* @Author jidi
* @Email jidi_jidi@163.com
* @Date 2021/8/1
*/
@Component
public class AsyncComponent {
@Autowired
private UserService userService;
/**
* 无返回值的异步方法
*/
@Async
public void print(){
System.out.println(Thread.currentThread().getName() + "执行了方法print()" + System.currentTimeMillis());
}
/**
* 有返回值的异步方法
*/
@Async
public Future<Integer> asyncSquare(int x) {
System.out.println("calling asyncSquare," + Thread.currentThread().getName() + "," + new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("asyncSquare Finished," + Thread.currentThread().getName() + "," + new Date());
return new AsyncResult<>(x + x);
}
}
4、测试
package com.jidi.springbootredis;
import com.jidi.springbootredis.component.AsyncComponent;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* @Description @Async注解使用测试
* @Author jidi
* @Email jidi_jidi@163.com
* @Date 2021/8/1
*/
@Slf4j
@SpringBootTest
public class AsyncTest {
@Autowired
private AsyncComponent asyncComponent;
/**
* 测试无返回值的异步方法
*/
@Test
public void test1(){
for (int i = 0; i < 100 ; i++) {
asyncComponent.print();
}
}
/**
* 测试有返回值的异步方法
*/
@Test
public void test2() throws ExecutionException, InterruptedException {
Future<Integer> future = asyncComponent.asyncSquare(23);
while (true){
if(future.isCancelled()){
log.info("异步方法已经被取消!");
break;
}
if (future.isDone()){
log.info("异步方法已经执行完毕,执行结果为:" + future.get());
break;
}
}
}
}