SpringBoot 异步任务-使用@Async注解

常见的高并发方案

  • 异步,削峰填谷
  • 缓存,缓存相对稳定高频热点数据
  • 并行,缩短业务响应时间
  • 优化你的业务代码
  • 限流和降级,保护你的核心服务在高并发下能正常工作

异步场景

关联业务的执行结果对主线程的返回结果没有直接影响或无影响。此时,能让主线程更顺畅的执行,并给客户带来好的客户体验,可以将该关联业务做异步处理或类似的处理(如:消息队列)

@Async

该工具提供方便快捷的异步化执行业务的能力,只需要添加一个注解@Async既可以使你的业务异步执行,这里的异步执行,指的是新开一个线程业务;该注解可以使用到类上,也可以使用在方法上。

  • @EnableAsync启用异步化能力注解,推荐该注解配置在springboot的Config类或者启动类上
  • @Async开启异步化模式注解
  • AsyncConfigurer全局配置接口,用于配置自定义异步线程池和异步线程执行异常捕获器
  • AsyncUncaughtExceptionHandler异步化运行时全局异常捕获接口
  • AsyncExecutor异步化执行线程池,自定义异步执行线程池

注意事项

  • SpringBootApplication启动类添加@EnableAsync注解;
  • @Async使用
    • 类或者方法中使用@Async注解,类上标有该注解表示类中方法都是异步方法,方法上标有该注解表示方法是异步方法
    • @Async(“threadPool”),threadPool为自定义线程池,这样可以保证主线程中调用多个异步任务时能更高效的执行
  • 在定义异步方法的同一个类中,调用带有@Async注解方法,该方法则无法异步执行
  • 注解的方法必须是public方法,不能是static
  • @Async注解的实现和@Transactional一样,都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的,所以要经过Spring容器管理,否则调用方法的是对象本身而不是代理对象

无参数异步化接口示例

@Async
public void executeTask(){
    //业务操作
}

带参数异步化接口 

@Async
public Future task2(){
    //业务操作,T代指实体类/Object
    
    //返回操作结果
    return new AsyncResult<>(new User("zhangsan", 22));
}

自定义线程池

@Configuration
@EnableAsync
public class ExecutorConfig {
    private int corePoolSize = 10;
    private int maxPoolSize = 50;
    private int queueCapacity = 10;
 
    @Bean
    public Executor testAsync() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setThreadNamePrefix("MgmtExecutor-");
 
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

使用 

@Async("testAsync") // 自定义执行线程池

测试

100次调用,循环加一百减一百,并对计算时间随机处理,a最终结果应为6

controller

    @PostMapping("/test")
    public CommonResult test(@RequestBody ThirdPayParam param) {
        for (int i = 0; i < 100; i++) {
            testUtil.executeTask(i%2 == 0 ?100 : -100);
        }
        return CommonResult.success(null);
    }

TestUtil

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class TestUtil {

    public static int a = 6;
    @Async
    public void executeTask(int b){
        System.out.println(a);
        try {
            Thread.sleep((long) ((Math.random() + 1) * 1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        a = a + b;
        System.out.println("最终结果" + a);
    }
}

 SpringBoot 异步任务-使用@Async注解_第1张图片

嗯,后续再补充

你可能感兴趣的