SpringBoot異步Async使用Future與CompletableFuture區(qū)別小結(jié)
主要區(qū)別:
- Future:在執(zhí)行結(jié)束后沒法回調(diào),調(diào)用get方法會被阻塞;
- CompletableFuture:在執(zhí)行結(jié)束后可通過whenComplete或whenCompleteAsync方法回調(diào),不會阻塞線程,同時也是支持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 { /** * 異步任務(wù)自定義線程池 */ @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測試:
主線程等待各個異步執(zhí)行的線程返回的結(jié)果來做下一步操作,則必須阻塞在future.get()的地方等待結(jié)果返回,這時候又變成同步了。適用于需要等異步結(jié)果的場景。
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()+"進行任務(wù)futureTest1..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任務(wù)futureTest1完成"); return new AsyncResult<String>("這是任務(wù)futureTest1返回結(jié)果"); } @Async public Future<String> futureTest2(){ System.out.println(Thread.currentThread().getName()+"進行任務(wù)futureTest2..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任務(wù)futureTest2完成"); return new AsyncResult<String>("這是任務(wù)futureTest2返回結(jié)果"); } }
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(); //添加結(jié)果集,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()完成后才會執(zhí)行,因為get()方法會阻塞 System.out.println("map集合: "+map.size()); System.out.println("回調(diào)后的任務(wù): "+Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); } System.out.println("耗時: "+(System.currentTimeMillis() - beginTime)); return "success"; } }
打印結(jié)果
自定義線程-1進行任務(wù)futureTest1...
自定義線程-2進行任務(wù)futureTest2...
自定義線程-1任務(wù)futureTest1完成
這是任務(wù)futureTest1返回結(jié)果
自定義線程-2任務(wù)futureTest2完成
這是任務(wù)futureTest2返回結(jié)果
map集合: 2
回調(diào)后的任務(wù): http-nio-8082-exec-1
耗時: 5068
大家可以看到,這時候map集合里面是有值的,主線程http-nio-8082-exec-1是在異步執(zhí)行完才執(zhí)行的,因為get方法是會阻塞線程的。耗時5秒是以異步中耗時最長的方法為準,因為要等耗時最長的方法執(zhí)行完,才能合并。
2.CompletableFuture測試:
實現(xiàn)了Future和CompletionStage接口,保留了Future的優(yōu)點,并且彌補了其不足。即異步的任務(wù)完成后,需要用其結(jié)果繼續(xù)操作時,無需等待。適用于不需要等異步結(jié)果的場景。
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()+"進行任務(wù)completableFuture1..."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任務(wù)completableFuture1完成"); return CompletableFuture.completedFuture("這是任務(wù)completableFuture1返回結(jié)果"); } @Async public CompletableFuture<String> completableFuture2(){ System.out.println(Thread.currentThread().getName()+"進行任務(wù)completableFuture2..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"任務(wù)completableFuture2完成"); return CompletableFuture.completedFuture("這是任務(wù)completableFuture2返回結(jié)果"); } }
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(); //添加結(jié)果集,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); }); //這里不用等前面的結(jié)果集,會異步先執(zhí)行 System.out.println("map集合: "+map.size()); System.out.println("回調(diào)后的任務(wù): "+Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); } System.out.println("耗時: "+(System.currentTimeMillis() - beginTime)); return "success"; } }
打印結(jié)果
map集合: 0
回調(diào)后的任務(wù): http-nio-8082-exec-1
耗時: 33
自定義線程-1進行任務(wù)completableFuture1...
自定義線程-2進行任務(wù)completableFuture2...
自定義線程-1任務(wù)completableFuture1完成
這是任務(wù)completableFuture1返回結(jié)果自定義線程-1
自定義線程-2任務(wù)completableFuture2完成
這是任務(wù)completableFuture2返回結(jié)果自定義線程-2
大家可以看到,這時候map集合里面是空的,主線程http-nio-8082-exec-1是在異步之前打印的,說明使用whenComplete是異步的,不會阻塞線程的。耗時33毫秒不用等異步執(zhí)行完就能打印。
這里簡單說下whenComplete和whenCompleteAsync的區(qū)別:
whenComplete:執(zhí)行完當(dāng)前任務(wù)的線程,繼續(xù)執(zhí)行 whenComplete 的任務(wù)。
whenCompleteAsync: 執(zhí)行完當(dāng)前任務(wù)的線程,把whenCompleteAsync 的任務(wù)繼續(xù)提交給線程池來執(zhí)行。(可能開啟新的線程)
把前面的改成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); });
打印結(jié)果
map集合: 0
回調(diào)后的任務(wù): http-nio-8082-exec-1
耗時: 33
自定義線程-1進行任務(wù)completableFuture1...
自定義線程-2進行任務(wù)completableFuture2...
自定義線程-1任務(wù)completableFuture1完成
這是任務(wù)completableFuture1返回結(jié)果Thread-4
自定義線程-2任務(wù)completableFuture2完成
這是任務(wù)completableFuture2返回結(jié)果Thread-5
區(qū)別的地方在于Thread-4和Thread-5,這是新開的線程,不是線程池中的線程了。
總結(jié):
Future與CompletableFuture使用場景不一樣,都支持get方法,如果異步執(zhí)行完后需要同步,使用Future,反之,如果異步執(zhí)行完后,不需要等待,直接異步操作,那么使用CompletableFuture。
到此這篇關(guān)于SpringBoot異步Async使用Future與CompletableFuture區(qū)別小結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot Future CompletableFuture內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中優(yōu)化大量if...else...方法總結(jié)
在我們平時的開發(fā)過程中,經(jīng)常可能會出現(xiàn)大量If else的場景,代碼顯的很臃腫,非常不優(yōu)雅,下面這篇文章主要給大家介紹了關(guān)于java中優(yōu)化大量if...else...方法的相關(guān)資料,需要的朋友可以參考下2023-03-03springboot啟動腳本start.sh和停止腳本 stop.sh的詳細教程
這篇文章主要介紹了springboot啟動腳本start.sh和停止腳本 stop.sh的詳細教程,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08