SpringBoot异步Async使用Future与CompletableFuture区别小结
作者:可乐汉堡cola
主要区别:
- Future:在执行结束后没法回调,调用get方法会被阻塞;
- CompletableFuture:在执行结束后可通过whenComplete或whenCompleteAsync方法回调,不会阻塞线程,同时也是支持get方法的;
代码示例
spring boot配置Async,@EnableAsync启动异步
AsyncConfig
package com.test.config; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @EnableAsync @Configuration public class AsyncConfig { /** * 异步任务自定义线程池 */ @Bean(name="taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(50); executor.setMaxPoolSize(500); executor.setQueueCapacity(300); executor.setKeepAliveSeconds(60); executor.setThreadNamePrefix("自定义线程-"); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setWaitForTasksToCompleteOnShutdown(true); executor.setAwaitTerminationSeconds(60); return executor; } }
1.Future测试:
主线程等待各个异步执行的线程返回的结果来做下一步操作,则必须阻塞在future.get()的地方等待结果返回,这时候又变成同步了。适用于需要等异步结果的场景。
FutureService
package com.test.service; import java.util.concurrent.Future; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; import org.springframework.stereotype.Service; @Service public class FutureService { @Async public Future<String> futureTest1(){ System.out.println(Thread.currentThread().getName()+"进行任务futureTest1..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任务futureTest1完成"); return new AsyncResult<String>("这是任务futureTest1返回结果"); } @Async public Future<String> futureTest2(){ System.out.println(Thread.currentThread().getName()+"进行任务futureTest2..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任务futureTest2完成"); return new AsyncResult<String>("这是任务futureTest2返回结果"); } }
FutureController
package com.test.controller; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.test.service.FutureService; @RestController @RequestMapping("/future") public class FutureController { @Autowired private FutureService futureService; //超时时间 public static final long timeout = 30; @RequestMapping(value = "futureTest", method = RequestMethod.GET) public String futureTest() { // 开始时间戳 long beginTime = System.currentTimeMillis(); Future<String> result1 = futureService.futureTest1(); Future<String> result2 = futureService.futureTest2(); //添加结果集,30秒超时 Map<String, Object> map = new HashMap<String, Object>(); try { String str1 = result1.get(timeout, TimeUnit.SECONDS); System.out.println(str1); String str2 = result2.get(timeout, TimeUnit.SECONDS); System.out.println(str2); map.put("result1", str1); map.put("result2", str2); //这里需要等get()完成后才会执行,因为get()方法会阻塞 System.out.println("map集合: "+map.size()); System.out.println("回调后的任务: "+Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); } System.out.println("耗时: "+(System.currentTimeMillis() - beginTime)); return "success"; } }
打印结果
自定义线程-1进行任务futureTest1...
自定义线程-2进行任务futureTest2...
自定义线程-1任务futureTest1完成
这是任务futureTest1返回结果
自定义线程-2任务futureTest2完成
这是任务futureTest2返回结果
map集合: 2
回调后的任务: http-nio-8082-exec-1
耗时: 5068
大家可以看到,这时候map集合里面是有值的,主线程http-nio-8082-exec-1是在异步执行完才执行的,因为get方法是会阻塞线程的。耗时5秒是以异步中耗时最长的方法为准,因为要等耗时最长的方法执行完,才能合并。
2.CompletableFuture测试:
实现了Future和CompletionStage接口,保留了Future的优点,并且弥补了其不足。即异步的任务完成后,需要用其结果继续操作时,无需等待。适用于不需要等异步结果的场景。
CompletableFutureService
package com.test.service; import java.util.concurrent.CompletableFuture; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class CompletableFutureService { @Async public CompletableFuture<String> completableFuture1(){ System.out.println(Thread.currentThread().getName()+"进行任务completableFuture1..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任务completableFuture1完成"); return CompletableFuture.completedFuture("这是任务completableFuture1返回结果"); } @Async public CompletableFuture<String> completableFuture2(){ System.out.println(Thread.currentThread().getName()+"进行任务completableFuture2..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任务completableFuture2完成"); return CompletableFuture.completedFuture("这是任务completableFuture2返回结果"); } }
CompletableFutureController
package com.test.controller; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.test.service.CompletableFutureService; @RestController @RequestMapping("/completable") public class CompletableFutureController { @Autowired private CompletableFutureService completableFutureService; @RequestMapping(value = "completableFutureTest", method = RequestMethod.GET) public String CompletableFutureTest() { // 开始时间戳 long beginTime = System.currentTimeMillis(); CompletableFuture<String> result1 = completableFutureService.completableFuture1(); CompletableFuture<String> result2 = completableFutureService.completableFuture2(); //添加结果集,30秒超时 Map<String, Object> map = new HashMap<String, Object>(); try { result1.whenComplete((r, t)->{ System.out.println(r+Thread.currentThread().getName()); map.put("result1", r); }); result2.whenComplete((r, t)->{ System.out.println(r+Thread.currentThread().getName()); map.put("result2", r); }); //这里不用等前面的结果集,会异步先执行 System.out.println("map集合: "+map.size()); System.out.println("回调后的任务: "+Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); } System.out.println("耗时: "+(System.currentTimeMillis() - beginTime)); return "success"; } }
打印结果
map集合: 0
回调后的任务: http-nio-8082-exec-1
耗时: 33
自定义线程-1进行任务completableFuture1...
自定义线程-2进行任务completableFuture2...
自定义线程-1任务completableFuture1完成
这是任务completableFuture1返回结果自定义线程-1
自定义线程-2任务completableFuture2完成
这是任务completableFuture2返回结果自定义线程-2
大家可以看到,这时候map集合里面是空的,主线程http-nio-8082-exec-1是在异步之前打印的,说明使用whenComplete是异步的,不会阻塞线程的。耗时33毫秒不用等异步执行完就能打印。
这里简单说下whenComplete和whenCompleteAsync的区别:
whenComplete:执行完当前任务的线程,继续执行 whenComplete 的任务。
whenCompleteAsync: 执行完当前任务的线程,把whenCompleteAsync 的任务继续提交给线程池来执行。(可能开启新的线程)
把前面的改成whenCompleteAsync测试一下
result1.whenCompleteAsync((r, t)->{ System.out.println(r+Thread.currentThread().getName()); map.put("result1", r); }); result2.whenCompleteAsync((r, t)->{ System.out.println(r+Thread.currentThread().getName()); map.put("result2", r); });
打印结果
map集合: 0
回调后的任务: http-nio-8082-exec-1
耗时: 33
自定义线程-1进行任务completableFuture1...
自定义线程-2进行任务completableFuture2...
自定义线程-1任务completableFuture1完成
这是任务completableFuture1返回结果Thread-4
自定义线程-2任务completableFuture2完成
这是任务completableFuture2返回结果Thread-5
区别的地方在于Thread-4和Thread-5,这是新开的线程,不是线程池中的线程了。
总结:
Future与CompletableFuture使用场景不一样,都支持get方法,如果异步执行完后需要同步,使用Future,反之,如果异步执行完后,不需要等待,直接异步操作,那么使用CompletableFuture。
到此这篇关于SpringBoot异步Async使用Future与CompletableFuture区别小结的文章就介绍到这了,更多相关SpringBoot Future CompletableFuture内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!