@Async异步注解

一、简单实用

@EnableAsync 启动异步注解

// Spring boot启用:
@EnableAsync
@EnableTransactionManagement
public class SettlementApplication {
    public static void main(String[] args) {
        SpringApplication.run(SettlementApplication.class, args);
    }
}

注意:使用@Async注解时,默认线程池为SimpleAsyncTaskExecutor

默认线程池的默认配置如下:

  1. 默认核心线程数:8;
  2. 最大线程数:Integet.MAX_VALUE;
  3. 队列使用LinkedBlockingQueue;
  4. 容量是:Integet.MAX_VALUE;
  5. 空闲线程保留时间:60s;
  6. 线程池拒绝策略:AbortPolicy;

但是阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确线程池的运行规则,规避资源耗尽的风险。

  • newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。

  • newCachedThreadPool和newScheduledThreadPool:要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

所以,使用@Async时,需要自定义线程池,结合项目实际情况给与配置。

二、@Async应用自定义线程池

自定义线程池,可对系统中线程池更加细粒度的控制,方便调整线程池大小配置,线程执行异常控制和处理。在设置系统自定义线程池代替默认线程池时,虽可通过多种模式设置,但替换默认线程池最终产生的线程池有且只能设置一个(不能设置多个类继承AsyncConfigurer)。自定义线程池有如下模式:

  • 重新实现接口AsyncConfigurer

  • 继承AsyncConfigurerSupport

  • 配置由自定义的TaskExecutor替代内置的任务执行器

  • springboot可直接在配置上文件上修改配置

方式一: 实现接口AsyncConfigurer

@Configuration
 public class AsyncConfiguration implements AsyncConfigurer {
     @Bean("kingAsyncExecutor")
     public ThreadPoolTaskExecutor executor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
         int corePoolSize = 10;
         executor.setCorePoolSize(corePoolSize);
         int maxPoolSize = 50;
         executor.setMaxPoolSize(maxPoolSize);
         int queueCapacity = 10;
         executor.setQueueCapacity(queueCapacity);
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
         String threadNamePrefix = "kingDeeAsyncExecutor-";
         executor.setThreadNamePrefix(threadNamePrefix);
         executor.setWaitForTasksToCompleteOnShutdown(true);
         // 使用自定义的跨线程的请求级别线程工厂类19         int awaitTerminationSeconds = 5;
         executor.setAwaitTerminationSeconds(awaitTerminationSeconds);
         executor.initialize();
         return executor;
     }
 
     @Override
     public Executor getAsyncExecutor() {
         return executor();
     }
 
     @Override
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return (ex, method, params) -> ErrorLogger.getInstance().log(String.format("执行异步任务'%s'", method), ex);
     }
 }

方式二:继承AsyncConfigurerSupport

@Configuration  
@EnableAsync  
class SpringAsyncConfigurer extends AsyncConfigurerSupport {  
  
    @Bean  
    public ThreadPoolTaskExecutor asyncExecutor() {  
        ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();  
        threadPool.setCorePoolSize(3);  
        threadPool.setMaxPoolSize(3);  
        threadPool.setWaitForTasksToCompleteOnShutdown(true);  
        threadPool.setAwaitTerminationSeconds(60 * 15);  
        return threadPool;  
    }  
  
    @Override  
    public Executor getAsyncExecutor() {  
        return asyncExecutor;  
}  

  @Override  
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return (ex, method, params) -> ErrorLogger.getInstance().log(String.format("执行异步任务'%s'", method), ex);
}
}

方式三:配置自定义的TaskExecutor

@EnableAsync
 @Configuration
 public class TaskPoolConfig {
     @Bean(name = AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME)
     public Executor taskExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
          //核心线程池大小
         executor.setCorePoolSize(10);
         //最大线程数
         executor.setMaxPoolSize(20);
         //队列容量
         executor.setQueueCapacity(200);
         //活跃时间
         executor.setKeepAliveSeconds(60);
         //线程名字前缀
         executor.setThreadNamePrefix("taskExecutor-");
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
         return executor;
     }
    @Bean(name = "new_task")
     public Executor taskExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
          //核心线程池大小
         executor.setCorePoolSize(10);
         //最大线程数
         executor.setMaxPoolSize(20);
         //队列容量
         executor.setQueueCapacity(200);
         //活跃时间
         executor.setKeepAliveSeconds(60);
         //线程名字前缀
         executor.setThreadNamePrefix("taskExecutor-");
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
         return executor;
     }
 }

多个线程池时,@Async注解,使用系统默认或者自定义的线程池(代替默认线程池)。可在项目中设置多个线程池,在异步调用时,指明需要调用的线程池名称,如@Async("new_task")

方式四:springboot的方式重新配置

spring:
  task:
    execution:
      pool:
        max-size: 10
        core-size: 5
        keep-alive: 3s
        queue-capacity: 1000
        thread-name-prefix: my-executor

好的,下面是一个简单的示例: 假设我们有一个 UserService,其中有一个方法 sendEmail,它需要异步地发送电子邮件。现在我们想要将当前用户的信息透传给异步任务中使用的线程。 首先,我们需要在异步方法上添加 @Async 注解,并在配置类中启用异步支持: ```java @Configuration @EnableAsync public class AppConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(100); executor.setQueueCapacity(10); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new CustomAsyncExceptionHandler(); } } ``` 在上面的示例中,我们创建了一个 ThreadPoolTaskExecutor,它将用于执行异步任务。我们还实现了 AsyncConfigurer 接口,并覆盖了 getAsyncExecutor 和 getAsyncUncaughtExceptionHandler 方法,以提供自定义的 Executor 和异常处理程序。 现在我们需要将当前用户信息存储在一个 ThreadLocal 对象中。这可以通过一个拦截器来实现: ```java public class UserContextInterceptor extends HandlerInterceptorAdapter { private final ThreadLocal<String> userThreadLocal = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String currentUser = request.getHeader("X-User"); userThreadLocal.set(currentUser); return true; } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { userThreadLocal.remove(); } public String getCurrentUser() { return userThreadLocal.get(); } } ``` 在上面的示例中,我们创建了一个 UserContextInterceptor,它将在每个请求的开始和结束时执行。在 preHandle 方法中,我们从请求头中获取当前用户信息,并将其存储在一个 ThreadLocal 对象中。在 afterCompletion 方法中,我们将删除该信息,以避免内存泄漏。 现在,我们可以在 UserService 的 sendEmail 方法中使用 UserContextInterceptor 中存储的当前用户信息: ```java @Service public class UserService { @Autowired private JavaMailSender mailSender; @Autowired private UserContextInterceptor userContextInterceptor; @Async public void sendEmail(String to, String subject, String text) { String currentUser = userContextInterceptor.getCurrentUser(); // 使用当前用户信息发送电子邮件 // ... } } ``` 在上面的示例中,我们使用 @Autowired 注解将 UserContextInterceptor 注入到 UserService 中。在 sendEmail 方法中,我们从 UserContextInterceptor 中获取当前用户信息,并在发送电子邮件时使用它。 通过这种方式,我们可以将当前用户信息透传给异步任务中使用的线程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值