同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。 异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行下面的流程。
在Java中,一般在处理类似的场景之时,都是基于创建独立的线程去完成相应的异步调用逻辑, 通过主线程和不同的线程之间的执行流程,从而在启动独立的线程之后,主线程继续执行而不会产生停滞等待的情况。
在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候, 将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。
在spring中,通过任务执行器,也就是TaskExecutor来实现多线程和并发编程。 使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。 而实际开发中任务一般是非阻碍的,也就是非异步的,所以我们要在配置类中通过@EnableAsync开启对异步任务的支持, 并通过在实际执行的Bean的方法中使用@Async注解来声明其是一个异步任务。
/** * 通过重写getAsyncExecutor方法,制定默认的任务执行由该方法产生 * * 配置类实现AsyncConfigurer接口并重写getAsyncExcutor方法,并返回一个ThreadPoolTaskExevutor * 这样我们就获得了一个基于线程池的TaskExecutor */ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(CORE_POOL_SIZE);// 线程池维护线程的最少数量 taskExecutor.setMaxPoolSize(MAX_POOL_SIZE);// 线程池维护线程的最大数量 taskExecutor.setQueueCapacity(QUEUE_CAPACITY);// 线程池所使用的缓冲队列 taskExecutor.initialize(); return taskExecutor; }
一、configuration包下的配置类,实现AsyncConfigurer接口
package com.liu.configuration; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; /** * 利用@EnableAsync注解开启异步任务支持 */ @Configuration @EnableAsync public class TaskExecutorConfig implements AsyncConfigurer { /** * Set the ThreadPoolExecutor's core pool size. */ private static final int CORE_POOL_SIZE = 5; /** * Set the ThreadPoolExecutor's maximum pool size. */ private static final int MAX_POOL_SIZE = 20; /** * Set the capacity for the ThreadPoolExecutor's BlockingQueue. */ private static final int QUEUE_CAPACITY = 10; /** * 通过重写getAsyncExecutor方法,制定默认的任务执行由该方法产生 * * 配置类实现AsyncConfigurer接口并重写getAsyncExcutor方法,并返回一个ThreadPoolTaskExevutor * 这样我们就获得了一个基于线程池的TaskExecutor */ @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); // 线程池维护线程的最少数量 taskExecutor.setCorePoolSize(CORE_POOL_SIZE); // 线程池维护线程的最大数量 taskExecutor.setMaxPoolSize(MAX_POOL_SIZE); // 线程池所使用的缓冲队列 taskExecutor.setQueueCapacity(QUEUE_CAPACITY); taskExecutor.initialize(); return taskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } /** * 自定义任务执行器:在定义了多个任务执行器的情况下,可以使用@Async("getMineAsync")来设定 * * @return */ @Bean public Executor getMineAsync() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(CORE_POOL_SIZE - 4); executor.setMaxPoolSize(MAX_POOL_SIZE - 10); executor.setQueueCapacity(QUEUE_CAPACITY - 5); executor.setThreadNamePrefix("mineAsync-"); // rejection-policy:当pool已经达到max size的时候,如何处理新任务 // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }
二、业务层
package com.liu.service; import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Component; /** * 操作计算 service 类:简单实现有关异步和同步两种计算方式的性能比较 */ @Component public class ArithmeticService { private final static Logger logger = LoggerFactory.getLogger(ArithmeticService.class); public static final int DoTime = 5000; /** * 异步任务 只需要在所需实现异步的方法上加上@Async注解, 并通过Future<T>来接受异步方法的处理结果 * 通过@Async注解表明该方法是个异步方法,如果注解在类级别,则表明该类所有的方法都是异步方法 * * @return */ @Async public Future<Long> subByAsync() throws Exception { long start = System.currentTimeMillis(); long sum = 0; Thread.sleep(DoTime); long end = System.currentTimeMillis(); sum = end - start; logger.info("\t 完成任务一"); return new AsyncResult<>(sum); } /** * 仅使用异步注解的方式实现异步方法 * * @return */ @Async public void subByVoid() throws Exception { long start = System.currentTimeMillis(); long sum = 0; Thread.sleep(DoTime); long end = System.currentTimeMillis(); sum = end - start; logger.info("\t 完成任务二 "); logger.info("注解任务执行的时间是: " + sum + "(毫秒)"); } /** * 使用同步计算的方式--同步调用 * * @return */ public long subBySync() throws Exception { long start = System.currentTimeMillis(); long sum = 0; Thread.sleep(DoTime); long end = System.currentTimeMillis(); sum = end - start; logger.info("\t 完成任务三 "); return sum; } @Async("getMineAsync") public void doMineAsync(int i) throws Exception { System.out.println("------\t:" + i); Thread.sleep(10000); } }
三、controller层代码
package com.liu.controller; import java.util.concurrent.Future; import com.liu.service.ArithmeticService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/async") public class IndexController { private final static Logger logger = LoggerFactory.getLogger(IndexController.class); @Autowired private ArithmeticService arithmeticService; @GetMapping("/test1") public void index() { long start = System.currentTimeMillis(); try { logger.info("--------------------------------------------\n"); logger.info("每个任务执行的时间是:" + arithmeticService.DoTime + "(毫秒)"); Future<Long> task = arithmeticService.subByAsync(); arithmeticService.subByVoid(); long sync = arithmeticService.subBySync(); while (true) { if (task.isDone()) { long async = task.get(); logger.info("异步任务执行的时间是:" + async + "(毫秒)"); // logger.info("注解任务执行的时间是: -- (毫秒)"); logger.info("同步任务执行的时间是:" + sync + "(毫秒)"); break; } } logger.info("--------------------------------------------\n"); } catch (Exception e) { e.printStackTrace(); } long end = System.currentTimeMillis(); logger.info("\t........请求响应时间为:" + (end - start) + "(毫秒)"); } /** * 自定义实现线程异步 */ @GetMapping("/test2") public void mineAsync() { for (int i = 0; i < 100; i++) { try { arithmeticService.doMineAsync(i); } catch (Exception e) { e.printStackTrace(); } } } }