⭐@Async 注解:
使用@Async
注解需要在SpringBoot
启动类上加上注解:
// Spring Boot 启用: @EnableAsync @EnableTransactionManagement public class AsyncApplication { public static void main(String[] args) { SpringApplication.run(AsyncApplication.class, args); } }
@Async
应用默认线程池。Spring
应用默认的线程池,指在@Async
注解在使用时,不指定线程池的名称。查看源码,@Async
的默认线程池为SimpleAsyncTaskExecutor
。
无返回值调用 :
基于@Async
无返回值调用,直接在使用类,使用方法(建议在使用方法)上,加上注解。若需要抛出异常,需手动new
一个异常抛出。
/** * 带参数的异步调用 异步方法可以传入参数 * 对于返回值是void,异常会被AsyncUncaughtExceptionHandler处理掉 * @param s */ @Async public void asyncInvokeWithException(String s) { log.info("asyncInvokeWithParameter, parementer={}", s); throw new IllegalArgumentException(s); }
有返回值Future调用:
/** * 异常调用返回Future * 对于返回值是Future,不会被AsyncUncaughtExceptionHandler处理,需要我们在方法中捕获异常并处理 * 或者在调用方在调用Futrue.get时捕获异常进行处理 * * @param i * @return */ @Async public Future<String> asyncInvokeReturnFuture(int i) { log.info("asyncInvokeReturnFuture, parementer={}", i); Future<String> future; try { Thread.sleep(1000 * 1); future = new AsyncResult<String>("success:" + i); throw new IllegalArgumentException("a"); } catch (InterruptedException e) { future = new AsyncResult<String>("error"); } catch(IllegalArgumentException e){ future = new AsyncResult<String>("error-IllegalArgumentException"); } return future; }
默认线程池的弊端:
在线程池应用中,参考阿里巴巴 Java 开发规范:线程池不允许使用Executors
去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor
的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。Executors
各个方法的弊端:
newFixedThreadPool
和newSingleThreadExecutor
:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM
。
newCachedThreadPool
和newScheduledThreadPool
:要问题是线程数最大数是Integer.MAX_VALUE
,可能会创建数量非常多的线程,甚至OOM
。
@Async
默认异步配置使用的是SimpleAsyncTaskExecutor
,该线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError
错误。针对线程创建问题,SimpleAsyncTaskExecutor
提供了限流机制,通过concurrencyLimit
属性来控制开关,当concurrencyLimit>=0
时开启限流机制,默认关闭限流机制即concurrencyLimit=-1
,当关闭情况下,会不断创建新的线程来处理任务。基于默认配置,SimpleAsyncTaskExecutor
并不是严格意义的线程池,达不到线程复用的功能。
@Async应用自定义线程池:
@Configuration @EnableAsync public class BaseAsyncConfigurer implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //核心线程池数量,方法: 返回可用处理器的Java虚拟机的数量。 executor.setCorePoolSize(Runtime.getRuntime().availableProcessors()); //最大线程数量 executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors()*5); //线程池的队列容量 executor.setQueueCapacity(Runtime.getRuntime().availableProcessors()*2); //线程名称的前缀 executor.setThreadNamePrefix("this-excutor-"); // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务 // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行 //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } /*异步任务中异常处理*/ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return (Throwable ex, Method method, Object... params)->{ //todo 异步方法异常处理 System.out.println("class#method: " + method.getDeclaringClass().getName() + "#" + method.getName()); System.out.println("type : " + ex.getClass().getName()); System.out.println("exception : " + ex.getMessage()); }; } }
@Async 失效:
- 异步方法使用
static
修饰 - 异步类没有使用
@Component
注解(或其他注解)导致spring
无法扫描到异步类 - 异步方法不能与异步方法在同一个类中
- 类中需要使用
@Autowired
或@Resource
等注解自动注入,不能自己手动new
对象 - 如果使用
SpringBoot
框架必须在启动类中增加@EnableAsync
注解 - 在
Async
方法上标注@Transactional
是没用的。 在Async
方法调用的方法上标注@Transactional
有效。