简介应用场景同步:同步是整个处理过程的顺序执行,每一个过程完成后返回结果。异步:异步调用只是发送调用指令,调用者不需要等待被调用方法执行完毕;相反,它继续执行以下过程。比如在某个调用中,需要依次调用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在FutureSpring//基于Java配置的启用方法:@配置@EnableAsyncpublicclassSpringAsyncConfig{...}//启用Springboot:@EnableAsync@EnableTransactionManagementpublicclassSettlementApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SettlementApplication.class,args);}}@Async应用默认线程池Spring应用默认线程池,这意味着使用@Async注解时没有指定线程池的名称。查看源码,@Async默认的线程池是SimpleAsyncTaskExecutor。无返回值调用是在@Async无返回值调用的基础上,直接在use类和use方法上加上注解(推荐use方法)。如果需要抛出异常,需要手动new一个异常来抛出。/***带参数的异步调用可以传入参数*返回值为void,异常会由AsyncUncaughtExceptionHandler处理*@params*/@AsyncpublicvoidaasyncInvokeWithException(Strings){log.info("asyncInvokeWithParameter,parementer={}",s);thrownewIllegalArgumentException(s);}有一个返回值Futurecall/***exceptioncallreturnsFuture*对于返回值为Future,不会被AsyncUncaughtExceptionHandler处理,我们需要在方法中捕获异常和handle*或call方捕获异常并在调用Future.get时处理它**@parami*@return*/@AsyncpublicFutureasyncInvokeReturnFuture(inti){log.info("asyncInvokeReturnFuture,parementer={}",i);Future未来;try{Thread.sleep(1000*1);future=newAsyncResult("success:"+i);thrownewIllegalArgumentException("a");}catch(InterruptedExceptione){future=newAsyncResult("error");}catch(IllegalArgumentExceptione){future=newAsyncResult("error-IllegalArgumentException");}returnfuture;}带返回值的CompletableFuture调用CompletableFuture没有使用@Async注解,可以实现调用系统线程池处理业务的功能。JDK5增加了Future接口,用于描述异步计算的结果。Future和相关的使用方法虽然提供了异步执行任务的能力,但是获取结果非常不方便,只能通过阻塞或者轮询的方式获取任务的结果。阻塞方式显然违背了我们异步编程的初衷,轮询方式会消耗不必要的CPU资源,无法及时得到计算结果。CompletionStage代表了异步计算过程中的某个阶段。一个阶段完成后,可能会触发另一个阶段。阶段的计算执行可以是Function、Consumer或Runnable。例如:stage.thenApply(x->square(x)).thenAccept(x->System.out.print(x)).thenRun(()->System.out.println())一个阶段的执行可能是单个阶段完成触发,也可能是多个阶段一起触发。在Java8中,CompletableFuture提供了非常强大的Future扩展功能,可以帮助我们简化异步编程的复杂度,提供函数式编程能力。计算结果可以通过回调进行处理,同时也提供了CompletableFutures转换和合并的方法。它可能代表一个明确完成的Future,也可能代表一个完成阶段(CompletionStage),支持在计算完成后触发一些功能或执行某些动作。实现了Future和CompletionStage接口/***数据查询线程池*/privatestaticfinalThreadPoolExecutorSELECT_POOL_EXECUTOR=newThreadPoolExecutor(10,20,5000,TimeUnit.MILLISECONDS,newLinkedBlockingQueue<>(1024),newThreadFactoryBuilder().setNameFormat("selectThreadPool"Executor-%d).build());//tradeMapper.countTradeLog(tradeSearchBean)方法表示获取数量,返回值为int//获取总数CompletableFuturecountFuture=CompletableFuture.supplyAsync(()->tradeMapper.countTradeLog(tradeSearchBean),SELECT_POOL_EXECUTOR);//同步阻塞CompletableFuture.allOf(countFuture).join();//获取结果intcount=countFuture.get();默认线程池的缺点是在线程池应用中,参考阿里巴巴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@ConfigurationpublicclassAsyncConfigurationimplementsAsyncConfigurer{@Bean("kingAsyncExecutor")publicThreadPoolTask??Executorexecutor(){ThreadPoolTask??Executorexecutor=newThreadPoolTask??Executor();intcorePoolSize=10;executor.setCorePoolSize(corePoolSize);intmaxPoolSize=50;executor.setMaxPoolSize(maxPoolSize);intqueueCapacity=10;executor.setQueCapacity(queuecapacity);executor.setRejecteDexeCutionHandler(newthreadPoolexecutor.callerrunspolicy());strignthreadnameprefix=“kingdeeasyncexecutor-”kiddeeasyncexecutor-';工厂类19intawaitTerminationSeconds=5;executor.setAwaitTerminationSeconds(awaitTerminationSeconds);executor.initialize();returnexecutor;}@OverridepublicExecutorgetAsyncExecutor(){returnexecutor();}@OverridepublicAsyncUncaughtExceptionHandlergetAsyncUncaughtExceptionHandler(),parameter(ex,)-...(3);threadPool.setMaxPoolSize(3);threadPool.setWaitForTasksToCompleteOnShutdown(true);threadPool.setAwaitTerminationSeconds(60*15);returnthreadPool;}@OverridepublicExecutorgetAsyncExecutor(){returnasyncExecutor;}@OverridepublicAsyncUncaughtExceptionHandlergetAsyncUncaughtExceptionHandler(mex,(){return,(){返回)->ErrorLogger.getInstance().log(String.format("Executeasynchronoustask'%s'",method),ex);}}配置自定义TaskExecutor由于源码中AsyncConfigurer的默认线程池为空,Spring首先通过beanFactory.getBean(TaskExecutor.class)检查是否有线程池。未配置时,使用beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME,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。替换默认线程池时,可以不指定线程池名称。@EnableAsync@ConfigurationpublicclassTaskPoolConfig{@Bean(name=AsyncExecutionAspectSupport.DEFAULT_TASK_EXECUTOR_BEAN_NAME)publicExecutortaskExecutor(){ThreadPoolTask??Executorexecutor=newThreadPoolTask??MaxExecutor();//核心线程池最大尺寸executor.setCorePoolSize/threads(10)Capexe0)/QueetQueuecapacity);//活跃时间executor.setKeepAliveSeconds(60);//线程名前缀executor.setThreadNamePrefix("taskExecutor-");executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy());returnexecutor;}@Bean(name="new_task")publicExecutortaskExecutor(){ThreadPoolTask??Executorexecutor=newThreadPoolTask??Executor();//核心线程池大小executor.setCorePoolSize(10);//最大线程数executor.setMaxPoolSize(20);//队列容量executor.setQueueCapacity(200);//活跃时间executor.setKeepAliveSeconds(60);//线程名称前缀executor.setThreadNamePrefix("taskExecutor-");executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy());returnexecutor;}}多线程池@异步笔记解决方法,使用系统默认或自定义线程池(替换默认线程池)可以在项目中设置多个线程池。异步调用时,指定要调用的线程池名称,如@Async("new_task")。@Async部分重要源码解析源码——获取线程池方法源码——设置默认线程池defaultExecutor,默认为空,重新实现接口AsyncConfigurer的getAsyncExecutor()时,可以设置默认线程水池。源码-当找不到项目中设置的默认线程池时,使用spring默认线程池。