说一说Springboot中使用异步@Async注解的一些流程以及注意事项。

# 1.在主应用类上添加@EnableAsync注解,启用异步支持


@SpringBootApplication
@EnableAsync
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

# 2.创建一个单独的异步执行类

@Service
public class AsyncService {

    @Async
    public void asyncMethod() {
        // 异步方法的代码
    }
}

# 3.在其他类的方法中调用它

@RestController
public class MyController {
    @Autowired
    private AsyncService asyncService;

    @GetMapping("/test")
    public String test() {
        // 执行异步任务
        asyncService.asyncMethod();
        return "ok";
    }
}

  • 不要在同一个类中直接调用被@Async注解的方法,因为这会导致方法同步执行。
  • 当一个异步方法B调用另一个异步方法A时,如果A也是异步的,则B的执行不会等待A完成。
  • 在异步方法中,事务会被异步地传播,这意味着如果父事务回滚,子事务(即异步方法)可能不会回滚。

还要注意:
当使用@Async注解时,默认情况下会使用一个简单的SimpleAsyncTaskExecutor来执行异步方法。但是,SimpleAsyncTaskExecutor并不是真正的线程池,它每次执行都会创建一个新的线程,这可能会导致系统资源耗尽,单机情况可以采用默认,但高并发情况推荐自定义创建一个线程池,例如创建一个“taskExecutor”名字的线程池。

# 创建全局线程池方式

@Configuration
//如果在主类文件MyApp中添加了下面注解,就不需要了,全局一个就行
//@EnableAsync
public class ThreadPoolConfig {

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
       executor.setCorePoolSize(2); // 核心线程数
        executor.setQueueCapacity(20); // 队列容量
        executor.setMaxPoolSize(5); // 最大线程数
//并发情况,先走核心线程,然后走队列,满了再创建最大线程数。总数也就是2+20+5
        executor.setAllowCoreThreadTimeOut(true);//允许核心线程超时销毁
        executor.setKeepAliveSeconds(60); // 设置空闲线程的存活时间
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());//拒绝策略,丢弃旧任务
        executor.setThreadNamePrefix("Async-");
        executor.initialize();
        return executor;
    }
}

可以在方法中单独指定某个线程池:@Async("taskExecutor")

@Service
public class AsyncService {

    @Async("taskExecutor")
    public void asyncMethod() {
         // 这里将会使用注入的taskExecutor线程池执行异步方法
    }
}

也可以全局配置,比如在启动类@EnableAsync注解中指定默认的线程池Bean名称

@SpringBootApplication
@EnableAsync("taskExecutor")
public class MyApp {

}


@Service
public class AsyncService {
// 所有异步方法都会去taskExecutor线程池执行
    @Async
    public void asyncMethod1() {
         
    }
    @Async
    public void asyncMethod2() {
         
    }
}

异步方法返回类型问题:
如果异步方法的返回类型不是void,调用者得到的将是代表异步操作的Future,而不是操作的实际结果。

@Async
public Future<String> asyncMethod() {}