@Async异步任务

1. 简单介绍

Spring异步线程池的接口类,其实质是java.util.concurrent.Executor

Spring 已经实现的异常线程池:

  1. SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
  2. SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方
  3. ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类
  4. SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类
  5. ThreadPoolTaskExecutor :最常使用,推荐!!! 其实质是对java.util.concurrent.ThreadPoolExecutor的包装

如果你想要异步处理,则需在对应的方法上加上注解**@Async**,则会启动一个新的线程去执行。

2. 异步配置方式

SpringBoot中开启异步支持非常简单,只需要在配置类上面加上注解 @EnableAsync,同时定义自己的线程池即可。 也可以不定义自己的线程池,则使用系统默认的线程池

这里推荐两种异步配置的方式:

方式1: 定义配置类,实现AsyncConfigurerc接口

@Configuration
@EnableAsync
public class AsyncConfiguration implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(100);
        executor.setQueueCapacity(100);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60 * 10);
        executor.setThreadNamePrefix("async");
        executor.initialize(); //需要进行初始化!!!
        return executor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        //这里可以自定义异常处理器
        return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
    }
}

方式2: 定义配置类,直接注册ThreadPoolTaskExecutor

@EnableAsync
@Configuration
public class AsyncConfig {

    @Bean
    public AsyncTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("async-demo");
        executor.setMaxPoolSize(10);
        executor.setMaxPoolSize(100);
        executor.setQueueCapacity(100);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60 * 10);
        return executor;
    }
}

其中,关于线程池的设置有:

  • corePoolSize: 核心线程数,当向线程池提交一个任务时池里的线程数小于核心线程数,那么它会创建一个线程来执行这个任务,一直直到池内的线程数等于核心线程数
  • maxPoolSize: 最大线程数,线程池中允许的最大线程数量。关于这两个数量的区别我会在下面解释
  • queueCapacity: 缓冲队列大小,用来保存阻塞任务队列(注意这里的队列放的是任务而不是线程)
  • keepAliveSeconds: 允许线程存活时间(空闲状态下),单位为秒,默认60s
  • namePrefix: 线程名前缀
  • RejectedExecutionHandler拒绝策略,当线程池达到最大线程数时,如何处理新任务。线程池为我们提供的策略有
    • AbortPolicy:默认策略。直接抛出RejectedExecutionExecption异常
    • DiscardPolicy直接丢弃掉被拒绝的任务,且不会抛出任何异常
    • DiscardOldestPolicy丢弃掉队列中的队头元素(也就是最早在队列里的任务),然后重新执行 提交该任务 的操作
    • CallerRunsPolicy由主线程自己来执行这个任务,该机制将减慢新任务的提交
    • 或者可以自定义拒绝策略…

3. 异步任务类:

然后,我们需要定义自己的异步任务方法,这里创建了一个异步任务类,进行模拟:

@Component
@Slf4j
public class AsyncDemo {
    @Async
    public void async01() {
        log.info("简单的异步调用...");
    }

    @Async
    public void async02(String param) {
        log.info("带参数的异步调用,参数:{}...", param);
    }

    @Async
    public Future<String> async03(int i) {
        log.info("带回调函数的异步调用...");
        Future<String> future;
        try {
         Thread.sleep(1000);
         future = new AsyncResult<String>("success" + i);
        } catch (InterruptedException e) {
            future = new AsyncResult<String>("error");
        }
        return future;
    }
}

测试代码:

@SpringBootTest
class AsyncDemoApplicationTests {

    @Autowired
    private AsyncDemo asyncDemo;

    @Test
    void contextLoads() throws ExecutionException, InterruptedException {
        System.out.println("主线程执行...");
        asyncDemo.async01();
        asyncDemo.async02("test");
        Future<String> future = asyncDemo.async03(100);
        System.out.println(future.get());
    }
}

输出结果:

主线程执行...
2022-09-26 14:07:51.853  INFO 15814 --- [         async1] com.feng.async.demo01.AsyncDemo          : 简单的异步调用...
2022-09-26 14:07:51.853  INFO 15814 --- [         async3] com.feng.async.demo01.AsyncDemo          : 带回调函数的异步调用...
2022-09-26 14:07:51.853  INFO 15814 --- [         async2] com.feng.async.demo01.AsyncDemo          : 带参数的异步调用,参数:test...
success100

注意事项:

  • @Async修饰的方法必须为public方法
  • **@Async注解修饰的方法一定需要在其他类中进行调用,即在该异步方法所在类的外部进行调用,如果在该类的内部进行调用,则异步方法将会失效,变成同步!!!**这是因为@Async注解的实现是基于Spring的AOP,而AOP的实现又是通过动态代理实现的。
  • 异步方法的返回值只能为void或者Future
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值