目錄
引入
使用Future獲得異步執行結果時,要麼調用阻塞方法get(),要麼輪詢看isDone()是否爲true,這兩種方法都不是很好,因爲主線程也會被迫等待。
從Java 8開始引入了CompletableFuture,它針對Future做了改進,可以傳入回調對象,當異步任務完成或者發生異常時,自動調用回調對象的回調方法。
優勢如下:
- 可以利用結果進行級聯的執行
- 會自動回調給調用者
- 執行一批任務時,可以按照任務執行的順序,獲得結果
- 可以並行的獲取結果,只拿最先獲取的結果級聯的執行
簡介
CompleableFuture依然是對Executor的封裝,看構造函數的源碼,可以知道一般情況下會創建一個ForkJoinPool,同時ThreadFactory會設置爲守護線程。這就意味着:一旦主線程結束,線程池就會關閉。
簡單實用
public class CompletableFutureTest {
public static void main(String[] args) throws InterruptedException {
CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).whenComplete((v,t)->{
System.out.println("Done");
});
Thread.currentThread().join();
}
}
結果:
Done
可以發現CompleableFuture,在執行完成後,會自動去調用下個任務的方法,不會受到阻塞。
創建CompleableFuture
創建CompleableFuture
不建議使用構造方法,而是使用靜態的工廠方法構建。
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
public static <U> CompletableFuture<U> completedFuture(U value)
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor)
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
-
allOf(CompletableFuture<?>... cfs)
:這個方法會返回一個全新的CompletableFuture,同時會把傳遞進去的所有CompletableFuture執行完纔算是執行完成 -
anyOf(CompletableFuture<?>... cfs)
:這個方法會返回一個全新的CompletableFuture,只要傳遞進去的一個CompletableFuture執行完,就算是執行完成 -
completedFuture(U value)
:可以假設一個執行出了一個結果,進行下面的級聯操作 -
runAsync
:異步的執行Runnable -
supplyAsync
:異步的執行Supplier實例,會有回調的結果U
supplyAsync舉例
public class CompletableFutureTest {
public static void main(String[] args) throws InterruptedException {
supplyAsync();
Thread.currentThread().join();
}
private static void supplyAsync() throws InterruptedException {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello ");
CompletableFuture<String> future2 = future1.thenApplyAsync(obj -> {
try {
System.out.print(obj);
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "World! ";
});
future2.thenAccept(System.out::println);
}
}
結果:
Hello World!
分析:
需要給supplyAsync
提供一個Supplier接口,如下所示:
public interface Supplier<T> {
T get();
}
runAsync舉例
public class CompletableFutureTest {
public static void main(String[] args) throws InterruptedException {
supplyAsync();
Thread.currentThread().join();
}
private static void supplyAsync() throws InterruptedException {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> System.out.print("Hello "));
CompletableFuture<Void> future2 = future1.thenAccept(t -> {
System.out.println("World !");
});
}
}
結果:
Hello World!
分析:
比一般的提交一個Runnable相比,可以更加靈活點使用,級聯、並聯等操作
anyOf 舉例
同時從新浪和網易查詢證券代碼,只要任意一個返回結果,就進行下一步查詢價格,查詢價格也同時從新浪和網易查詢,只要任意一個返回結果,就完成操作:
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
// 兩個CompletableFuture執行異步查詢:
CompletableFuture<String> cfQueryFromSina = CompletableFuture.supplyAsync(() -> {
return queryCode("中國石油", "https://finance.sina.com.cn/code/");
});
CompletableFuture<String> cfQueryFrom163 = CompletableFuture.supplyAsync(() -> {
return queryCode("中國石油", "https://money.163.com/code/");
});
// 用anyOf合併爲一個新的CompletableFuture:
CompletableFuture<Object> cfQuery = CompletableFuture.anyOf(cfQueryFromSina, cfQueryFrom163);
// 兩個CompletableFuture執行異步查詢:
CompletableFuture<Double> cfFetchFromSina = cfQuery.thenApplyAsync((code) -> {
return fetchPrice((String) code, "https://finance.sina.com.cn/price/");
});
CompletableFuture<Double> cfFetchFrom163 = cfQuery.thenApplyAsync((code) -> {
return fetchPrice((String) code, "https://money.163.com/price/");
});
// 用anyOf合併爲一個新的CompletableFuture:
CompletableFuture<Object> cfFetch = CompletableFuture.anyOf(cfFetchFromSina, cfFetchFrom163);
// 最終結果:
cfFetch.thenAccept((result) -> {
System.out.println("price: " + result);
});
// 主線程不要立刻結束,否則CompletableFuture默認使用的線程池會立刻關閉:
Thread.sleep(200);
}
static String queryCode(String name, String url) {
System.out.println("query code from " + url + "...");
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
}
return "601857";
}
static Double fetchPrice(String code, String url) {
System.out.println("query price from " + url + "...");
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
}
return 5 + Math.random() * 20;
}
}
結果:
query code from https://finance.sina.com.cn/code/...
query code from https://money.163.com/code/...
query price from https://finance.sina.com.cn/price/...
query price from https://money.163.com/price/...
price: 7.50078323021774
分析:
需要注意一點,雖然是異步的從一個地方取值,但是其他任務依然會執行完成,而並非不再執行了
組合方法
組合兩個任務,同時處理兩個結果
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,
BiConsumer<? super T,? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T,? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,
BiConsumer<? super T,? super U> action,
Executor executor)
舉例:
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Void> future = completableFuture.thenAcceptBoth(CompletableFuture.supplyAsync(() -> 100),
(s,i)-> System.out.println("s: "+ s + ", i: " + i));
Thread.currentThread().join();
}
}
結果:
s: Hello, i: 100
分析
- 可以看出是兩個任務組合,然後同時將兩個結果一起處理
組合兩個任務,任務完成後做的操作
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,
Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
Runnable action)
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,
Runnable action)
分析
- 相當於組合兩個任務,執行
thenRun
當兩個任務任意一個執行完成後,執行一個操作
public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
Runnable action)
public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
Runnable action)
舉例
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello");
return "a";
});
CompletableFuture<Void> future = completableFuture.runAfterEitherAsync(CompletableFuture.supplyAsync(() -> {
System.out.println(100);
return 100;
}),
() -> System.out.println("done."));
Thread.currentThread().join();
}
}
結果:
Hello
100
done.
分析
- 相當於一次同步任務
組合兩個任務,處理後,返回一個結果
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,
BiFunction<? super T,? super U,? extends V> fn)
舉例
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "a");
CompletableFuture<Boolean> future = completableFuture.thenCombine(CompletableFuture.supplyAsync(() -> 100),
(s,i)->{
System.out.println("s: " + s +" , i : " + i);
return true;
});
System.out.println(future.get());
Thread.currentThread().join();
}
}
結果:
s: a , i : 100
true
合併兩個任務,第一個任務的輸出是第二個任務的輸入
public <U> CompletableFuture<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T,? extends CompletionStage<U>> fn,
Executor executor)
分析
- 相當於一次級聯操作
中轉方法
有返回值
當執行完成時執行的操作
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
舉例
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future = completableFuture.whenComplete((v, t) -> System.out.println(v + " World !"));
System.out.println(future.get());
Thread.currentThread().join();
}
}
結果
Hello World !
Hello
分析
- 當執行完成時執行的回調方法
- 該方法會接收執行的結果以及異常
- 回調完成會,會把任務執行的結果傳遞回去
- whenCompleteAsync是異步的;whenComplete是同步的,會卡住主線程
- 需要傳遞一個
BiConsumer
接口,如下所示:
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
- T是執行的結果,U是執行時產生的異常
級聯操作
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn,Executor executor)
舉例
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Integer> future = completableFuture.thenApplyAsync(t -> {
String s = t + " World !";
System.out.println(s);
return s.length();
});
System.out.println(future.get());
Thread.currentThread().join();
}
}
public interface Function<T, R> {
R apply(T t);
}
結果
Hello World !
13
分析
- 是一個級聯操作,即拿着上個任務的結果,做下個任務,同時返回一個新的結果
處理結果的操作
public <U> CompletableFuture<U> handle(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn)
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T,Throwable,? extends U> fn,Executor executor)
舉例
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Integer> future = completableFuture.handleAsync((s,t) -> {
String aaa = t + " World !";
System.out.println(aaa);
return aaa.length();
});
System.out.println(future.get());
Thread.currentThread().join();
}
}
結果:
Hello World !
13
分析:
- 相比於
whenComplete
返回值可以自己處理,相當於一次級聯 - 相比於
thenApply
,可以處理異常
無返回值
處理結果
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
舉例
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Void> future = completableFuture.thenAccept(t -> {
String aaa = t + " World !";
System.out.println(aaa);
});
System.out.println(future.get());
Thread.currentThread().join();
}
}
結果
Hello World !
null
分析
- 相當於一次級聯,但是沒有返回值
執行完全部任務
public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
分析
- 相較
thenAccept
,不處理任務的執行結果
終結方法
處理異常
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn)
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
int a = 1/0;
return "World ";
});
completableFuture.exceptionally(Throwable::getMessage).thenAccept(t->{
System.out.println(t);
});
Thread.currentThread().join();
}
}
立馬獲取結果
public T getNow(T valueIfAbsent)
舉例
public class CompletableFutureTest {
public static void main(String[] args) throws Exception {
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> "World ");
Thread.sleep(1000);
String now = completableFuture.getNow("Hello");
System.out.println(now);
System.out.println(completableFuture.get());
Thread.currentThread().join();
}
}
結果
World
World
分析
- 如果結果完成返回結果,如果未完成,返回傳入進去的值
判斷結果是否完成,如果未完成則賦予結果
public boolean complete(T value)
判斷結果是否完成,如果未完成返回異常
public boolean completeExceptionally(Throwable ex)
後續獲取結果會產生異常
public void obtrudeException(Throwable ex)
總結
thenAccept
()處理正常結果;exceptionally
()處理異常結果;thenApplyAsync
()用於串行化另一個CompletableFuture
;anyOf
()和allOf
()用於並行化多個CompletableFuture
。