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

