对于异步方法调用,从Spring3开始就提供了@Async注解,可以在方法上标注,实现异步调用方法。调用者在调用时会立即返回,方法的实际执行会提交给SpringTaskExecutor的任务,由指定线程池中的线程执行。项目应用中@Async调用线程池,推荐使用自定义线程池方式。自定义线程池常用解决方案:重新实现接口AsyncConfigurer。应用场景同步:同步是指整个处理过程是按顺序执行的,每个过程执行完后返回结果。异步:异步调用只是发送调用指令,调用者不需要等待被调用方法执行完毕;相反,它继续执行以下过程。比如在某个调用中,需要依次调用A、B、C这三个流程方法;如果都是同步调用,需要在流程执行完成之前依次执行;如果B是一个异步的call方法,在A执行完之后,调用B,并不等待B执行完,而是开始调用C,C执行完之后,就意味着流程完成了。在Java中,在处理类似的场景时,一般都是基于创建一个独立的线程来完成相应的异步调用逻辑。通过主线程与不同业务子线程之间的执行过程,在启动独立线程后,主线程继续执行,不会停滞等待。Spring实现的线程池SimpleAsyncTaskExecutor:不是真正的线程池。此类不重用线程。默认情况下,每次调用都会创建一个新线程。SyncTaskExecutor:这个类没有实现异步调用,它只是一个同步操作。仅适用于不需要多线程的情况。ConcurrentTaskExecutor:Executor的适配类,不推荐。ThreadPoolTask??Executor不满足要求才考虑使用这个类。SimpleThreadPoolTask??Executor:Quartz的SimpleThreadPool类。仅当线程池同时被quartz和非quartz使用时才需要这个类。ThreadPoolTask??Executor:最常用,推荐。它的本质是java.util.concurrent.ThreadPoolExecutor的包装。异步方法最简单的异步调用,返回值为void。带参数的异步调用,异步方法可以传入参数和返回值,常规调用返回@Async@AsyncapplicationdefaultthreadpoolenabledinFutureSpringSpringapplicationdefaultthreadpool,也就是说线程池的名字不是在使用@Async注释时指定。查看源码,@Async默认的线程池是SimpleAsyncTaskExecutor。无返回值调用是在@Async无返回值调用的基础上,直接在use类和use方法上加上注解(推荐use方法)。如果需要抛出异常,需要手动new一个异常来抛出。FuturecallwithreturnvalueCompletableFuturecallwithreturnvalueCompletableFuture没有使用@Async注解,可以实现调用系统线程池处理业务的功能。JDK5增加了Future接口,用于描述异步计算的结果。Future和相关的使用方法虽然提供了异步执行任务的能力,但是获取结果非常不方便,只能通过阻塞或者轮询的方式获取任务的结果。阻塞方式显然违背了我们异步编程的初衷,轮询方式会消耗不必要的CPU资源,无法及时得到计算结果。CompletionStage代表了异步计算过程中的某个阶段。一个阶段完成后,可能会触发另一个阶段。阶段的计算执行可以是Function、Consumer或Runnable。例如:stage.thenApply(x->square(x)).thenAccept(x->System.out.print(x)).thenRun(()->System.out.println())一个stage的执行可能是单个阶段完成触发,也可能是多个阶段一起触发。在Java8中,CompletableFuture提供了非常强大的Future扩展功能,可以帮助我们简化异步编程的复杂度,提供函数式编程能力。计算结果可以通过回调进行处理,同时也提供了CompletableFutures转换和合并的方法。它可能代表一个明确完成的Future,也可能代表一个完成阶段(CompletionStage),支持在计算完成后触发一些功能或执行某些动作。它实现了Future和CompletionStage接口。默认线程池的缺点。在线程池应用中,参考阿里巴巴java开发规范:不允许使用Executors创建线程池,不允许使用系统默认线程池。推荐使用ThreadPoolExecutor方式,这样的处理方式可以让开发工程师明确线程池的运行规则,避免资源耗尽的风险。Executors:newFixedThreadPool和newSingleThreadExecutor的各个方法的缺点:主要问题是累积的请求处理队列可能会消耗非常大的内存,甚至OOM。newCachedThreadPool和newScheduledThreadPool:主要问题是最大线程数为Integer.MAX_VALUE,可能会创建非常多的线程,甚至OOM。@Async默认异步配置使用SimpleAsyncTaskExecutor。默认情况下,线程池为每个任务创建一个线程。如果系统中不断地创建线程,系统最终会占用过多的内存而导致OutOfMemoryError错误。针对线程创建的问题,SimpleAsyncTaskExecutor提供了限流机制,由concurrencyLimit属性控制。当concurrencyLimit>=0时,开启限流机制,默认关闭限流机制,即concurrencyLimit=-1。关闭时,将不断创建新的。线程来处理任务。基于默认配置,SimpleAsyncTaskExecutor严格来说不是线程池,无法实现线程复用的功能。@Async将自定义线程池应用于自定义线程池,可以更细粒度地控制系统中的线程池,方便调整线程池大小配置,线程执行异常控制和处理。在设置系统自定义线程池替换默认线程池时,虽然可以设置多种模式,但是替换默认线程池最终生成的线程池有且只能设置一个(不能设置多个继承AsyncConfigurer的类)自定义线程池有以下几种模式:重新实现接口AsyncConfigurer继承AsyncConfigurerSupport配置自定义的TaskExecutor来替代内置的任务执行器通过查看Spring源码中@Async的默认调用规则,实现AsyncConfigurer接口的类在会优先查找源码,优先考虑实现该接口的类,该类为AsyncConfigurerSupport。但是默认配置的线程池和异步处理方法都是空的,所以无论是继承还是重新实现接口,都需要指定一个线程池。并重新实现publicExecutorgetAsyncExecutor()方法。实现接口AsyncConfigurer并继承AsyncConfigurerSupport来配置一个自定义的TaskExecutor。由于源码中AsyncConfigurer默认的线程池是空的,所以Spring首先通过beanFactory.getBean(TaskExecutor.class)检查是否有线程池。Executor.class),查询是否有默认名称为TaskExecutor的线程池。所以在项目中,定义一个名为TaskExecutor的bean来生成一个默认的线程池。也可以声明一个线程池,不指定线程池的名字,底层本身是基于TaskExecutor.class的。例如:Executor.class:ThreadPoolExecutorAdapter->ThreadPoolExecutor->AbstractExecutorService->ExecutorService->Executor,最后一层是Executor.class,在替换默认线程池的时候需要设置默认线程池名称为TaskExecutorTaskExecutor.class:在ThreadPoolTask??Executor->SchedulingTaskExecutor->AsyncTaskExecutor->TaskExecutor的模式下,最终底层是TaskExecutor.class。替换默认线程池时,可以不指定线程池名称。
