前言
我们在开发中经常会对执行慢的方法或不涉及主业务的方法执行异步调用。
- 实现方式1: 可以自己手动创建原生的线程,实现异步。
- 实现方式2:也可以使用springboot的@Async实现异步。
本文主要说下springboot的@Async方式和多线程时如何自定义线程池,以及希望有返回结果时,如何用其内置的方法为我所用。
1.@Async异步调用
异步的原理: springboot会为代理对象创建一个线程,执行异步方法。
- 1.在springboot的入口函数处引入 开启异步自动配置注解
@EnableAsync
。
- 2书写异步方法
- 3.调用
在需要用到异步调用的地方,调用异步方法
特别注意
异步方法不可和调用它的类在一个类中,
因为@Async是springboot使用的代理对象来创建或者使用线程池中的线程处理,
如果
在一个类中就成同步了
。这点和不能调用同一个类的带有@Transactional方法是一个道理。
2.手动创建线程池管理@Async异步的线程
如果不手动创建线程池,springboot会用自己的线程池来处理,不过为了更好的控制线程,我们可以手动创建。springboot也给我们提供了配置方法。
我们可以这样配置。
@Configuration @EnableAsync public class ThreadPoolTaskConfig { /** * 核心线程数(默认线程数) */ private static final int corePoolSize = 10; /** * 最大线程数 */ private static final int maxPoolSize = 50; /** * 允许线程空闲时间(单位:默认为秒) */ private static final int keepAliveTime = 5; /** * 缓冲队列大小 */ private static final int queueCapacity = 200; /** * 线程池名前缀 */ private static final String threadNamePrefix = "Async-Service-"; @Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名 public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(keepAliveTime); executor.setThreadNamePrefix(threadNamePrefix); // 线程池对拒绝任务的处理策略 // CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 初始化 executor.initialize(); return executor; } }
然后在用到的异步方法的地方,显式引用改线程池。
备注
:
可以指定线程池的前缀threadNamePrefix,便于以后分析查看日志。如果需要创建多个线程池,指定不同的前缀,就可以快速定位出现问题的代码。
3.异步创建有返回值的调用
有时候我们不止希望异步执行任务,还希望任务执行完成后会有一个返回值,在java中提供了Future泛型接口,用来接收任务执行结果,springboot也提供了此类支持,使用实现了ListenableFuture接口的类如AsyncResult来作为返回值的载体。比如上例中,我们希望返回一个类型为String类型的值,可以将返回值改造为:
异步任务类
@Component public class AsyncTask { @Async public Future<String> task1() throws InterruptedException{ long currentTimeMillis = System.currentTimeMillis(); Thread.sleep(1000); long currentTimeMillis1 = System.currentTimeMillis(); System.out.println("task1任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms"); return new AsyncResult<String>("task1执行完毕"); } @Async public Future<String> task2() throws InterruptedException{ long currentTimeMillis = System.currentTimeMillis(); Thread.sleep(2000); long currentTimeMillis1 = System.currentTimeMillis(); System.out.println("task2任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms"); return new AsyncResult<String>("task2执行完毕"); } @Async public Future<String> task3() throws InterruptedException{ long currentTimeMillis = System.currentTimeMillis(); Thread.sleep(3000); long currentTimeMillis1 = System.currentTimeMillis(); System.out.println("task3任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms"); return new AsyncResult<String>("task3执行完毕"); } }
任务返回后使用:
@RequestMapping("") @RestController public class AsyncTaskController { @Autowired private AsyncTask asyncTask; @RequestMapping("") public String doTask() throws InterruptedException{ long currentTimeMillis = System.currentTimeMillis(); Future<String> task1 = asyncTask.task1(); Future<String> task2 = asyncTask.task2(); Future<String> task3 = asyncTask.task3(); String result = null; for (;;) { if(task1.isDone() && task2.isDone() && task3.isDone()) { // 三个任务都调用完成,退出循环等待 break; } Thread.sleep(1000); } long currentTimeMillis1 = System.currentTimeMillis(); result = "task任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms"; return result; } }
大功告成,完!