前言异步调用几乎是应对高并发、解决性能问题的常用手段。如何启动异步调用?SpringBoot中提供了一种非常简单的方式,就是注解@Async。今天我们重新认识了@Async,以及注意事项。简单使用新建三个job任务:@ServicepublicclassTaskDemo{privatestaticLoggerlogger=LoggerFactory.getLogger(TaskDemo.class);publicvoidexecute1(){logger.info("处理耗时任务1...开始");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务1......end");}publicvoidexecute2(){logger.info("处理耗时任务2...开始");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务2...结束");}publicvoidexecute3(){logger.info("正在处理耗时任务3......start");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务3...结束");}}测试代码:@RestControllerpublicclassTaskController{@AutowiredprivateTaskDemo任务演示;@GetMapping("/task/test")publicStringtestTask(){taskDemo.execute1();taskDemo.execute2();taskDemo.execute3();返回“确定”;上面的代码是同一个线程的同步执行,需要9秒才能完成springbootasynchronous的异步处理很简单,加2个注解即可@ServicepublicclassTaskDemo{privatestaticLoggerlogger=LoggerFactory.getLogger(TaskDemo.class);@Asyncpublicvoidexecute1(){logger.info("处理耗时任务1...开始");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务1......end");}@Asyncpublicvoidexecute2(){logger.info("正在处理耗时任务2...开始");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务2...结束");}@Asyncpublicvoidexecute3(){logger.info("正在处理耗时任务Task3...start");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("正在处理耗时任务3......结束");}}@SpringBootApplication@EnableAsyncpublic类DemoApp{publicstaticvoidmain(String[]args){SpringApplication.run(DemoApp.class,args);}}添加了@Async和@EnableAsync两个注解从执行结果来看,发现整个过程耗时3秒,3秒在某些场景下,我们需要知道异步处理的任务什么时候完成,需要做附加业务处理。例如:我们需要在三个任务都完成后提示用户@ServicepublicclassTaskDemo{privatestaticLoggerlogger=LoggerFactory.getLogger(TaskDemo.class);@AsyncpublicFutureexecute1(){logger.info("处理耗时任务1...开始");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务1...结束");returnnewAsyncResult<>("任务1成功");}@AsyncpublicFutureexecute2(){logger.info("处理耗时任务2......start");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务2...结束");returnnewAsyncResult<>("任务2成功");}@AsyncpublicFutureexecute3(){logger.info("正在处理耗时任务3...开始");尝试{TimeUnit。秒。睡觉(3);}赶上(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务3...结束");returnnewAsyncResult<>("任务3成功");}}@RestControllerpublicclassTaskController{privatestaticLoggerlogger=LoggerFactory.getLogger(TaskController.class);@AutowiredprivateTaskDemotaskDemo;@GetMapping("/task/test")publicStringtestTask()throwsInterruptedException{Futuretask1=taskDemo.execute1();Futuretask2=taskDemo.execute2();Futuretask3=taskDemo.execute3();while(true){if(task1.isDone()&&task2.isDone()&&task3.isDone()){中断;}TimeUnit.SECONDS.sleep(1);}logger.info(">>>>>>3个任务已经处理完毕");返回“确定”;}}执行结果发现在请求线程中提示用户3Alltaskshavebeenprocessed.这段代码做了什么改动:1.将具体任务的返回类型改为Future类型的对象2.调用任务时,循环判断任务是否处理完毕。自定义线程池说到异步处理,肯定要考虑线程池,什么是线程池,小伙伴可以网上补一下。@Async的线程池定义比较方便,直接上传代码://核心线程数taskPoolSecutor.setCore10);//线程池保持最大线程数,只有当缓冲队列满了才会申请超过核心线程数的线程taskExecutor.setMaxPoolSize(100);//缓存队列taskExecutor.setQueueCapacity(50);//允许空闲时间,当空闲时间到达后,超出核心线程的线程将被销毁taskExecutor.setKeepAliveSeconds(200);//异步方法内部线程名taskExecutor.setThreadNamePrefix("TaskPool-01-");/***当线程池的任务缓存队列已满,线程池中的线程数达到maximumPoolSize时,如果有任务过来,将采用任务拒绝策略*通常有以下四种策略:*ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。*ThreadPoolExecutor.DiscardPolicy:同样是丢弃任务,但不会抛出异常。*ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复这个过程)*ThreadPoolExecutor.CallerRunsPolicy:重新尝试添加当前任务,并自动重复调用execute()方法,直到它成功*/taskExecutor.setRejectedExecutionHandler(newThreadPoolExecutor.AbortPolicy());taskExecutor.setWaitForTasksToCompleteOnShutdown(true);taskExecutor.initialize();返回任务执行器;}@Bean(name="taskPool02Executor")publicThreadPoolTask??ExecutorgetTaskPool02Executor(){ThreadPoolTask??ExecutortaskExecutor=newThreadPoolTask??Executor();//核心线程数taskExecutor.setCorePoolSize(10);//线程池保持最大线程数,只有当缓冲队列满了才会申请超过核心线程数的线程taskExecutor.setMaxPoolSize(100);//缓存队列taskExecutor.setQueueCapacity(50);//允许空闲时间,当空闲时间到达后,超出核心线程的线程将被销毁taskExecutor.setKeepAliveSeconds(200);//异步方法内部线程名taskExecutor.setThreadNamePrefix("TaskPool-02-");/***当线程池的任务缓存队列已满,线程池线程数达到maximumPoolSize。如果还有任务过来,则采用任务拒绝策略*通常有以下四种策略:*ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException*ThreadPoolExecutor.DiscardPolicy:也丢弃任务,但不抛出异常。*ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复这个过程)*ThreadPoolExecutor.CallerRunsPolicy:重新尝试添加当前任务,并自动重复调用execute()方法,直到它成功*/taskExecutor.setRejectedExecutionHandler(newThreadPoolExecutor.AbortPolicy());taskExecutor.setWaitForTasksToCompleteOnShutdown(true);taskExecutor.initialize();返回任务执行器;}}定义了2个线程池Bean@ServicepublicclassTaskDemo{privatestaticLoggerlogger=LoggerFactoryDe??actory.getLogger(Task);@Async("taskPool01Executor")publicFutureexecute1(){logger.info("正在处理耗时任务1...开始");尝试{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务1...结束");returnnewAsyncResult<>("任务1成功");}@Async("taskPool01Executor")publicFutureexecute2(){logger.info("正在处理耗时任务2...开始");try{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务2...结束");returnnewAsyncResult<>("任务2ok");}@Async("taskPool02Executor")publicFutureexecute3(){logger.info("正在处理耗时任务3...开始");try{TimeUnit.SECONDS.sleep(3);}catch(InterruptedExceptione){e.printStackTrace();}logger.info("处理耗时任务3...结束");returnnewAsyncResult<>("任务3ok");}}@Async("线程池名称"),指定值使用自己定义的线程池:执行结果使用线程池注解(一定要注意)在使用@Async注解的时候,很多小伙伴都会被发现异步使用失败,主要原因是异步方法定义有问题1.异步方法不能用static修饰2.异步类没有使用@Component注解(或其他注解))这样spring就无法扫描到异步类3.异步方法和调用异步方法的方法不能在同一个类中4.类中需要自动注入@Autowired或@Resource等注解,new对象不能自己手动创建。5.如果使用SpringBoot框架,必须在启动类中加上@EnableAsync注解