比如在service类的一个方法上增加该注解,然后在controller类中调用该方法。注意不可以在本类调用,否则不起作用。
原理
原理是通过AOP实现。当Spring容器启动初始化bean时,判断类是否使用@Async注解,并为其创建切入点和切入点处理器,根据切入点创建代理。
当线程调用@Async注解声明的方法时,会调用代理,执行切入点处理器的invoke方法,将方法的执行提交给线程池中的另外一个线程来处理,从而实现异步执行。
需要注意一个错误用法,如果A方法调用同类的@Async注解声明的B方法,并不会异步执行。因为从A方法进入调用的都是该类对象实例本身的B方法,不会进入代理类的B方法。
测试方法:在调用类和被调用类增加logger.info(Thread.currentThread().getName() + “serviceName”); 判断两者是否是相同线程。
使用方式
在方法上使用该注解,声明该方法是一个异步任务。当其他线程调用这个方法时就会开启一个新的子线程去异步处理该业务逻辑。
在类上使用,声明该类的所有方法都是异步任务。
使用该注解的方法的类对象,必须是Spring管理下的bean对象,比如在类上声明@Component。
需要在主类上开启异步配置@EnableAsync
默认使用Spring的默认线程池SimpleAsyncTaskExecutor
核心线程数:8,
最大线程数:Integer.MAX_VALUE,
任务队列长度:Integer.MAX_VALUE,
任务队列:LinkedBlockingQueue,
空闲线程保留时间:60s,
线程池拒绝策略:AbortPolicy
问题:并发情况下会无限创建线程。有内存溢出的风险。
解决:自定义配置参数
Sping.task.execution.pool.max-size: 6
问题:因为@Async是异步执行,在其进行数据库操作时无法控制事务管理。
解决:把@Transactional注解放到内部的需要进行事务的方法上。
问题:如果在工程中共用线程池,可能导致因为一个业务占用了所有线程,导致其他业务的任务被abort。
解决:定义多个线程池。线程池隔离。
@Async(value = “myThreadpool”)
@Bean(“myThreadpool”)
Public Executor myThreadpool() {
}
@Async和CompletableFuture的使用场景
@Async
当业务流程有明显的主流程和次流程,次流程执行的结果不影响主流程。为了保证主流程的安全,或者提高主流程的性能,或者不影响主流程等等。
可以在Service A完成主流程,在Service B完成主流程。在Service A调用Service B。在Service B声明@Async。
CompletableFuture
在一个循环中的操作,使用CompletableFuture开启多个线程,多个线程之间的操作结果互不影响,这样提高性能。
了解CompletableFuture_云淡风轻~~的博客-CSDN博客
1.如果业务中每个线程需要在线程结束后立即进行处理,而非等待其他线程都结束再进行操作。
2.如果任务1和任务2并行执行,任何一个任务失败/成功,则标志整个业务失败/成功。
3.如果每个线程中有较为复杂的异步任务编排(组合多个Future并行执行结果),则建议使用CompletableFuture,因为可以简化代码,同时优雅。
CompletableFuture的优点就在于 :
①有回调函数,可以在执行完成后自动调用回调处理逻辑;
②其提供的四十多个API可以优雅的编排任务链(提供异步任务完成后的链式调用);
③具备异常处理机制
误区1:
CompletableFuture所有的方法都有额外以Async结尾的方法。此类方法的作用在于不是说 后续任务可以在前序任务还没执行完就可以运行。举例:
thenRun(Runnable action) 当上一个任务结束后,此任务沿用上一个任务的线程池。
thenRunAsync(Runnable action) 当上一个任务结束后,此任务使用默认的ForkJoinPool线程池
thenRunAsync(Runnable action,Executor executor),当上一个任务结束后,此任务使用自定义线程池(推荐)
误区2
因为默认使用forkJoinPool,可能出现因为线程池被其他任务占用且耗尽,导致本任务的线程长时间等待状态并阻塞。