使用@Async注解,在默认情况下用的是SimpleAsyncTaskExecutor线程池,该线程池不是真正意义上的线程池。
使用此线程池无法实现线程重用,每次调用都会新建一条线程。若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误,关键代码如下:

Spring的多种线程池:

  • SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
  • SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地
  • ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类
  • ThreadPoolTaskScheduler:可以使用cron表达式
  • ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装
  1. @Configuration
  2. @EnableAsync
  3. @Slf4j
  4. public class AsyncConfiguration implements AsyncConfigurer {
  5. @Bean(name = "asyncPoolTaskExecutor")
  6. public ThreadPoolTaskExecutor executor() {
  7. ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
  8. // 核心线程数
  9. taskExecutor.setCorePoolSize(10);
  10. // 线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
  11. taskExecutor.setMaxPoolSize(100);
  12. // 缓存队列
  13. taskExecutor.setQueueCapacity(50);
  14. // 许的空闲时间,当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
  15. taskExecutor.setKeepAliveSeconds(200);
  16. // 异步方法内部线程名称
  17. taskExecutor.setThreadNamePrefix("async-");
  18. // 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略 通常有以下四种策略:
  19. // ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  20. // ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
  21. // ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  22. // ThreadPoolExecutor.CallerRunsPolicy:重试添加当前的任务,自动重复调用 execute() 方法,直到成功
  23. taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  24. taskExecutor.initialize();
  25. return taskExecutor;
  26. }
  27. /** 指定默认线程池 */
  28. @Override
  29. public Executor getAsyncExecutor() {
  30. return executor();
  31. }
  32. @Override
  33. public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
  34. return (ex, method, params) -> log.error("线程池执行任务发送未知错误,执行方法:{}", method.getName(), ex);
  35. }
  36. }

引用