1.JAVA多線程(二十四)Java多線程之CompletableFuture類
1.1 什麼是Future
Future是Java 5添加的類,用來描述一個異步計算的結果。你可以使用isDone方法檢查計算是否完成,或者使用get阻塞住調用線程,直到計算完成返回結果,你也可以使用cancel方法停止任務的執行。
package com.yuanxw.chapter24;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class FutureExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 構造一個線程池
ExecutorService executorService = Executors.newCachedThreadPool();
// 提交線程任務
Future<Integer> future = executorService.submit(() -> {
try {
System.out.println(String.format("當前【%s】線程工作-開始...", Thread.currentThread().getName()));
TimeUnit.SECONDS.sleep(10);
System.out.println(String.format("當前【%s】線程工作-結束...", Thread.currentThread().getName()));
return ThreadLocalRandom.current().nextInt(100);
} catch (InterruptedException e) {
e.printStackTrace();
return -1;
}
});
System.out.println(String.format("【%s】在等待線程執行結果...", Thread.currentThread().getName()));
System.out.println("是否執行結束:"+future.isDone());
System.out.println(future.get());
System.out.println("是否執行結束:"+future.isDone());
}
}
執行結果:
當前【pool-1-thread-1】線程工作-開始...
【main】在等待線程執行結果...
是否執行結束:false
當前【pool-1-thread-1】線程工作-結束...
76
是否執行結束:true
1.2 Future的侷限性
雖然Future以及相關使用方法提供了異步執行任務的能力,但是對於結果的獲取卻是很不方便,只能通過阻塞或者輪詢的方式得到任務的結果。阻塞的方式顯然和我們的異步編程的初衷相違背,輪詢的方式又會耗費無謂的CPU資源,而且也不能及時地得到計算結果。CompletableFuture的出現解決了Future模式的缺點。
- 不能手動完成:
- 當你寫了一個函數,用於通過一個遠程API獲取一個電子商務產品最新價格。因爲這個 API 太耗時,你把它允許在一個獨立的線程中,並且從你的函數中返回一個 Future。現在假設這個API服務宕機了,這時你想通過該產品的最新緩存價格手工完成這個Future 。你會發現無法這樣做。
- Future的結果在非阻塞的情況下,不能執行更進一步的操作:
- Future 不會通知你它已經完成了,它提供了一個阻塞的 get() 方法通知你結果。你無法給 Future 植入一個回調函數,當 Future 結果可用的時候,用該回調函數自動的調用 Future 的結果。
- 多個Future不能串聯在一起組成鏈式調用:
- 有時候你需要執行一個長時間運行的計算任務,並且當計算任務完成的時候,你需要把它的計算結果發送給另外一個長時間運行的計算任務等等。你會發現你無法使用 Future 創建這樣的一個工作流。
不能組合多個 Future 的結果
假設你有10個不同的Future,你想並行的運行,然後在它們運行未完成後運行一些函數。你會發現你也無法使用 Future 這樣做。
- 有時候你需要執行一個長時間運行的計算任務,並且當計算任務完成的時候,你需要把它的計算結果發送給另外一個長時間運行的計算任務等等。你會發現你無法使用 Future 創建這樣的一個工作流。
- 沒有異常處理:
- Future API 沒有任務的異常處理結構居然有如此多的限制,幸好我們有CompletableFuture,你可以使用 CompletableFuture 達到以上所有目的。
1.3 什麼是CompletableFuture
在Java中CompletableFuture用於異步編程,異步編程是編寫非阻塞的代碼,運行的任務在一個單獨的線程,與主線程隔離,並且會通知主線程它的進度,成功或者失敗。在這種方式中,主線程不會被阻塞,不需要一直等到子線程完成。主線程可以並行的執行其他任務。使用這種並行方式,可以極大的提高程序的性能。
CompletableFuture 實現了 Future 和 CompletionStage接口,並且提供了許多關於創建,鏈式調用和組合多個 Future 的便利方法集,而且有廣泛的異常處理支持。
CompletableFuture和Java8的Stream搭配使用,使用對於一些並行訪問的耗時操作有很大的操作。
CompletableFuture繼承結構關係圖:
CompletableFuture類實現了CompletionStage和Future接口,所以你還是可以像以前一樣通過阻塞或者輪詢的方式獲得結果,儘管這種方式不推薦使用。
1.4 CompletableFuture方法分類
1.4.1. 創建CompletableFuture對象
以Async結尾並且沒有指定Executor的方法會使用ForkJoinPool.commonPool()作爲它的線程池執行異步代碼。runAsync方法也好理解,它以Runnable函數式接口類型爲參數,所以CompletableFuture的計算結果爲空。supplyAsync方法以Supplier<U>函數式接口類型爲參數,CompletableFuture的計算結果類型爲U。
CompletableFuture的靜態工廠方法:
方法名 | 描述 |
---|---|
runAsync(Runnable runnable) | 使用ForkJoinPool.commonPool()作爲它的線程池執行異步代碼。 |
runAsync(Runnable runnable, Executor executor) | 使用指定的thread pool執行異步代碼。 |
supplyAsync(Supplier supplier) | 使用ForkJoinPool.commonPool()作爲它的線程池執行異步代碼,異步操作有返回值。 |
supplyAsync(Supplier supplier, Executor executor) | 使用指定的thread pool執行異步代碼,異步操作有返回值。 |
- CompletableFuture.completedFuture是一個靜態輔助方法,用來返回一個已經計算好的CompletableFuture。
/**
* Returns a new CompletableFuture that is already completed with
* the given value.
*
* @param value the value
* @param <U> the type of the value
* @return the completed CompletableFuture
*/
public static <U> CompletableFuture<U> completedFuture(U value) {
return new CompletableFuture<U>((value == null) ? NIL : value);
}
- 而以下四個靜態方法用來爲一段異步執行的代碼創建CompletableFuture對象:
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the {@link ForkJoinPool#commonPool()} with
* the value obtained by calling the given Supplier.
*
* @param supplier a function returning the value to be used
* to complete the returned CompletableFuture
* @param <U> the function's return type
* @return the new CompletableFuture
*/
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the given executor with the value obtained
* by calling the given Supplier.
*
* @param supplier a function returning the value to be used
* to complete the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param <U> the function's return type
* @return the new CompletableFuture
*/
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the {@link ForkJoinPool#commonPool()} after
* it runs the given action.
*
* @param runnable the action to run before completing the
* returned CompletableFuture
* @return the new CompletableFuture
*/
public static CompletableFuture<Void> runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the given executor after it runs the given
* action.
*
* @param runnable the action to run before completing the
* returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @return the new CompletableFuture
*/
public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
1.4.2. 主動完成計算
方法名 | 描述 |
---|---|
get() | 方法同步等待結果。 |
get(timeout,unit) | get(timeout,unit)簽名方法,給定時間,然後返回其結果,如果超時,拋出異常 |
join() | 完成後返回結果值,如果完成異常,則返回(未檢查)異常。 |
getNow() | 如果已完成,則返回結果值(或拋出任何遇到的異常),否則返回給定的值IfAbsent。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CompletableFutureExample1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
runAsync();
// get() 方法同步等待結果。
get();
// get(timeout,unit)簽名方法,給定時間,然後返回其結果,如果超時,拋出異常
get(3,TimeUnit.SECONDS);
// join() 完成後返回結果值,如果完成異常,則返回(未檢查)異常。
join();
// getNow() 如果已完成,則返回結果值(或拋出任何遇到的異常),否則返回給定的值IfAbsent。
getNow();
}
/**
* runAsync()異步執行runAsync返回的CompletableFuture是沒有返回值的。
* supplyAsync()異步執行runAsync返回的CompletableFuture是有返回值的。
執行結果:
=====【main】執行runAsync()簽名方法-開始=====
Hello world!!!
=====【main】執行runAsync()簽名方法-結束=====
* @throws ExecutionException
* @throws InterruptedException
*/
private static void runAsync() throws ExecutionException, InterruptedException {
System.out.println(String.format("=====【%s】執行runAsync()簽名方法-開始=====", Thread.currentThread().getName()));
CompletableFuture.runAsync(() -> System.out.println("Hello world!!!")).get();
System.out.println(String.format("=====【%s】執行runAsync()簽名方法-結束=====", Thread.currentThread().getName()));
}
/**
get() 方法同步等待結果。
執行結果:
=====【main】執行get()簽名方法-開始=====
get()簽名方法,執行結果:4
=====【main】執行get()簽名方法-結束=====
* @throws ExecutionException
* @throws InterruptedException
*/
private static void get() throws ExecutionException, InterruptedException {
System.out.println(String.format("=====【%s】執行get()簽名方法-開始=====", Thread.currentThread().getName()));
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
// 睡眠5秒
processSleep(5);
return ThreadLocalRandom.current().nextInt(10) * 2;
});
/** get() 方法同步等待結果。 **/
System.out.println("get()簽名方法,執行結果:"+completableFuture.get());
System.out.println(String.format("=====【%s】執行get()簽名方法-結束=====", Thread.currentThread().getName()));
}
/**
get(timeout,unit)簽名方法,給定時間,然後返回其結果,如果超時,拋出異常
執行結果:
=====【main】執行get(timeout,unit)簽名方法-開始=====
java.util.concurrent.TimeoutException
at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1771)
at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1915)
at com.yuanxw.chapter24.CompletableFutureExample.get(CompletableFutureExample.java:53)
at com.yuanxw.chapter24.CompletableFutureExample.main(CompletableFutureExample.java:11)
=====【main】執行get(timeout,unit)簽名方法-結束=====
*
* @param timeout
* @param unit
* @throws ExecutionException
* @throws InterruptedException
*/
private static void get(long timeout, TimeUnit unit) throws ExecutionException, InterruptedException {
System.out.println(String.format("=====【%s】執行get(timeout,unit)簽名方法-開始=====", Thread.currentThread().getName()));
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
// 睡眠5秒
processSleep(5);
return ThreadLocalRandom.current().nextInt(10) * 2;
});
try {
System.out.println("get(timeout,unit)簽名方法,執行結果:"+completableFuture.get(timeout,unit));
} catch (TimeoutException e) {
e.printStackTrace();
}
System.out.println(String.format("=====【%s】執行get(timeout,unit)簽名方法-結束=====", Thread.currentThread().getName()));
}
/**
join() 完成後返回結果值,如果完成異常,則返回(未檢查)異常。
執行結果:
=====【main】執行join()簽名方法-開始=====
join()簽名方法,執行結果:40
=====【main】執行join()簽名方法-結束=====
* @throws ExecutionException
* @throws InterruptedException
*/
private static void join() throws ExecutionException, InterruptedException {
System.out.println(String.format("=====【%s】執行join()簽名方法-開始=====", Thread.currentThread().getName()));
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
// 睡眠5秒
processSleep(5);
return ThreadLocalRandom.current().nextInt(10) * 5;
});
/** join() 完成後返回結果值,如果完成異常,則返回(未檢查)異常。 **/
System.out.println("join()簽名方法,執行結果:"+ completableFuture.join());
System.out.println(String.format("=====【%s】執行join()簽名方法-結束=====", Thread.currentThread().getName()));
}
/**
getNow() 如果已完成,則返回結果值(或拋出任何遇到的異常),否則返回給定的值IfAbsent。
執行結果:
=====【main】執行getNow()簽名方法-開始=====
getNow()簽名方法,執行結果:===unknown===
getNow()簽名方法,執行結果:N
=====【main】getNow()簽名方法-結束=====
*/
private static void getNow() {
System.out.println(String.format("=====【%s】執行getNow()簽名方法-開始=====", Thread.currentThread().getName()));
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
// 睡眠5秒
processSleep(5);
// 生成隨機字符
char c = (char) (ThreadLocalRandom.current().nextInt(25 )+ 65);
return String.valueOf(c);
});
/** getNow() 如果已完成,則返回結果值(或拋出任何遇到的異常),否則返回給定的值IfAbsent。 **/
System.out.println("getNow()簽名方法,執行結果:"+ completableFuture.getNow("===unknown==="));
processSleep(6);
System.out.println("getNow()簽名方法,執行結果:"+ completableFuture.getNow("===unknown==="));
System.out.println(String.format("=====【%s】getNow()簽名方法-結束=====", Thread.currentThread().getName()));
}
/**
* 線程休眠的方法
* @param seconds
*/
private static void processSleep(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.4.3. 計算結果完成時的處理
當CompletableFuture計算結果完成時,我們需要對結果進行處理,或者當CompletableFuture產生異常的時候需要對異常進行處理。有如下幾種方法:
方法名 | 描述 |
---|---|
whenComplete(BiConsumer<? super T, ? super Throwable> action) | 當CompletableFuture完成計算結果時對結果進行處理,或者當CompletableFuture產生異常的時候對異常進行處理。 |
whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) | 當CompletableFuture完成計算結果時對結果進行處理,或者當CompletableFuture產生異常的時候對異常進行處理。使用ForkJoinPool。 |
BiConsumer<? super T, ? super Throwable> action, Executor executor) | 當CompletableFuture完成計算結果時對結果進行處理,或者當CompletableFuture產生異常的時候對異常進行處理。使用指定的線程池。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class CompletableFutureExample2 {
public static void main(String[] args) throws InterruptedException {
whenComplete();
Thread.currentThread().join();
}
/**
* runAsync()異步執行runAsync返回的CompletableFuture是沒有返回值的。
* supplyAsync()異步執行runAsync返回的CompletableFuture是有返回值的。
執行結果:
=====【main】執行runAsync()簽名方法-開始=====
Hello world!!!
=====【main】執行runAsync()簽名方法-結束=====
*/
/**
* whenComplete():當CompletableFuture完成計算結果時對結果進行處理,或者當CompletableFuture產生異常的時候對異常進行處理。
執行結果:
=====執行whenComplete()簽名方法-開始=====
執行supplyAsync()簽名方法-結束>>>>>
統計字符串長度:14
=====執行whenComplete()簽名方法-結束=====
*/
private static void whenComplete() {
System.out.println("=====執行whenComplete()簽名方法-開始=====");
CompletableFuture.supplyAsync(() ->{
// 睡眠5秒
processSleep(5);
System.out.println("執行supplyAsync()簽名方法-結束>>>>>");
return "Hello world!!!";
}).whenComplete((s, throwable) -> {
System.out.println("統計字符串長度:"+s.length());
System.out.println("=====執行whenComplete()簽名方法-結束=====");
});
}
/**
* 線程休眠的方法
* @param seconds
*/
private static void processSleep(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.4.4. 計算結果完成時的轉換
這裏同樣也是返回CompletableFuture,但是這個結果會由我們自定義返回去轉換他,同樣的不以Async結尾的方法由原來的線程計算,以Async結尾的方法由默認的線程池ForkJoinPool.commonPool()或者指定的線程池executor運行。
方法名 | 描述 |
---|---|
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) | 接受一個Function<? super T,? extends U>參數用來轉換CompletableFuture。 |
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) | 接受一個Function<? super T,? extends U>參數用來轉換CompletableFuture,使用ForkJoinPool。 |
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) | 接受一個Function<? super T,? extends U>參數用來轉換CompletableFuture,使用指定的線程池。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class CompletableFutureExample3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
thenApply();
}
/**
thenApply() 當原來的CompletableFuture計算完後,將結果傳遞給函數fn,將fn的結果作爲新的CompletableFuture計算結果。
因此它的功能相當於將CompletableFuture<T>轉換成CompletableFuture<U>。。
執行結果:
=====執行thenApply()簽名方法-開始=====
thenApply()簽名方法,在supplyAsync階段,執行結果:DFRHHQ
thenApply()簽名方法,執行結果:###【dfrhhq】###
=====thenApply()簽名方法-結束=====
*/
private static void thenApply() throws ExecutionException, InterruptedException {
System.out.println("=====執行thenApply()簽名方法-開始=====");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
// 睡眠5秒
processSleep(5);
StringBuffer buffer = new StringBuffer();
IntStream.range(0,6).boxed().forEach(integer -> {
// 生成隨機字符
char c = (char) (ThreadLocalRandom.current().nextInt(25 )+ 65);
buffer.append(c);
});
System.out.println("thenApply()簽名方法,在supplyAsync階段,執行結果:" + buffer.toString());
return buffer.toString();
}).thenApply(str -> String.format("###【%s】###", str.toLowerCase()));
System.out.println("thenApply()簽名方法,執行結果:"+ completableFuture.get());
System.out.println("=====thenApply()簽名方法-結束=====");
}
/**
* 線程休眠的方法
* @param seconds
*/
private static void processSleep(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.4.5. 計算結果完成時的消費
這裏還會有一個只會對計算結果消費不會返回任何結果的方法。同樣的不以Async結尾的方法由原來的線程計算,以Async結尾的方法由默認的線程池ForkJoinPool.commonPool()或者指定的線程池executor運行。
方法名 | 描述 |
---|---|
public CompletableFuture thenAccept(Consumer<? super T> action) | 當CompletableFuture完成計算結果,只對結果執行Action,而不返回新的計算值。 |
public CompletableFuture thenAcceptAsync(Consumer<? super T> action) | 當CompletableFuture完成計算結果,只對結果執行Action,而不返回新的計算值,使用ForkJoinPool。 |
public CompletableFuture thenAcceptAsync(Consumer<? super T> action,Executor executor) | 當CompletableFuture完成計算結果,只對結果執行Action,而不返回新的計算值。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class CompletableFutureExample4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
thenAccept();
}
/**
thenAccept() CompletableFuture完成計算結果,只對結果執行Action,而不返回新的計算值。
執行結果:
=====執行thenAccept()簽名方法-開始=====
系統自動生成的6位驗證碼:EHSRHT,該驗證碼5分鐘有效,請勿泄露給他人。
=====thenAccept()簽名方法-結束=====
*/
private static void thenAccept() throws ExecutionException, InterruptedException {
System.out.println("=====執行thenAccept()簽名方法-開始=====");
CompletableFuture
.supplyAsync(() -> "系統自動生成的6位驗證碼:")
.thenApply((str) -> str + verifiCode())
.thenApply((str) -> str + ",該驗證碼5分鐘有效,請勿泄露給他人。")
.thenAccept(System.out::println);
System.out.println("=====thenAccept()簽名方法-結束=====");
}
/**
* 生成6位數驗證碼
* @return
*/
private static String verifiCode(){
StringBuffer buffer = new StringBuffer();
IntStream.range(0,6).boxed().forEach(integer -> {
// 生成隨機字符
char c = (char) (ThreadLocalRandom.current().nextInt(25 )+ 65);
buffer.append(c);
});
return buffer.toString();
}
}
1.4.6. 計算結果完成時的消費合併結果
thenAcceptBoth以及相關方法提供了類似的功能,當兩個CompletionStage都正常完成計算的時候,就會執行提供的action,它用來組合另外一個異步的結果。
runAfterBoth是當兩個CompletionStage都正常完成計算的時候,執行一個Runnable,這個Runnable並不使用計算的結果。
方法名 | 描述 |
---|---|
public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action) | 當兩個CompletableFuture都正常完成後,執行提供的action,用它來組合另外一個CompletableFuture的結果。 |
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action) | 當兩個CompletableFuture都正常完成後,執行提供的action,用它來組合另外一個CompletableFuture的結果。使用ForkJoinPool。 |
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor) | 當兩個CompletableFuture都正常完成後,執行提供的action,用它來組合另外一個CompletableFuture的結果。使用指定的線程池。 |
public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other,Runnable action) | 當兩個CompletionStage都正常完成計算的時候,執行一個Runnable,這個Runnable並不使用計算的結果。 |
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action) | 當兩個CompletionStage都正常完成計算的時候,執行一個Runnable,這個Runnable並不使用計算的結果。使用ForkJoinPool。 |
public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other,Runnable action,Executor executor) | 當兩個CompletionStage都正常完成計算的時候,執行一個Runnable,這個Runnable並不使用計算的結果。使用指定的線程池。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class CompletableFutureExample5 {
public static void main(String[] args) throws InterruptedException {
thenAcceptBoth();
runAfterBoth();
Thread.currentThread().join();
}
/**
thenAcceptBoth() 當你想要使用兩個Future結果時,但不需要將任何結果值進行返回時,
可以用 thenAcceptBoth ,它表示後續的處理不需要返回值。
執行結果:
=====執行thenAcceptBoth()簽名方法-開始=====
Hello World
=====thenAcceptBoth()簽名方法-結束=====
*/
private static void thenAcceptBoth() {
System.out.println("=====執行thenAcceptBoth()簽名方法-開始=====");
CompletableFuture.supplyAsync(() -> "Hello")
.thenAcceptBoth(CompletableFuture.supplyAsync(() -> " World"),
(s1, s2) -> System.out.println(s1 + s2));
System.out.println("=====thenAcceptBoth()簽名方法-結束=====");
}
/**
* runAfterBoth是當兩個CompletionStage都正常完成計算的時候,
執行一個Runnable,這個Runnable並不使用計算的結果。
執行結果:
=====執行runAfterBoth()簽名方法-開始=====
ForkJoinPool.commonPool-worker-1線程,工作-開始>>
ForkJoinPool.commonPool-worker-2線程,工作-開始>>
=====runAfterBoth()簽名方法-結束=====
ForkJoinPool.commonPool-worker-2線程,工作-結束<<
ForkJoinPool.commonPool-worker-1線程,工作-結束<<
===執行任務結束===
*/
private static void runAfterBoth() {
System.out.println("=====執行runAfterBoth()簽名方法-開始=====");
CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "線程,工作-開始>>");
// 睡眠5秒
processSleep(5);
System.out.println(Thread.currentThread().getName() + "線程,工作-結束<<");
return 1;
}).runAfterBoth(CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "線程,工作-開始>>");
// 睡眠1秒
processSleep(1);
System.out.println(Thread.currentThread().getName() + "線程,工作-結束<<");
return 2;
}), () -> System.out.println("===執行任務結束==="));
System.out.println("=====runAfterBoth()簽名方法-結束=====");
}
/**
* 線程休眠的方法
* @param seconds
*/
private static void processSleep(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
1.4.7. 對計算結果的組合
對於Compose可以連接兩個CompletableFuture,其內部處理邏輯是當第一個CompletableFuture處理沒有完成時會合併成一個CompletableFuture,如果處理完成,第二個future會緊接上一個CompletableFuture進行處理。
方法名 | 描述 |
---|---|
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) | 當兩個CompletableFuture都正常完成後,執行提供的fn,用它來組合另外一個CompletableFuture的結果。 |
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) | 當兩個CompletableFuture都正常完成後,執行提供的fn,用它來組合另外一個CompletableFuture的結果。使用ForkJoinPool。 |
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor) | 當兩個CompletableFuture都正常完成後,執行提供的fn,用它來組合另外一個CompletableFuture的結果。使用指定的線程池。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureExample6 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
thenCombine();
}
/**
* 執行thenCombine()對於Compose可以連接兩個CompletableFuture,
* 其內部處理邏輯是當第一個CompletableFuture處理沒有完成時會合併成一個CompletableFuture,
* 如果處理完成,第二個future會緊接上一個CompletableFuture進行處理。
執行結果:
=====執行thenCombine()簽名方法-開始=====
thenCombine()簽名方法,執行結果:計算結果:30
=====thenCombine()簽名方法-結束=====
*/
private static void thenCombine() throws ExecutionException, InterruptedException {
System.out.println("=====執行thenCombine()簽名方法-開始=====");
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<String> completableFuture = future1.thenCombine(future2, (x, y) -> "計算結果:" + (x + y));
System.out.println("thenCombine()簽名方法,執行結果:"+ completableFuture.get());
System.out.println("=====thenCombine()簽名方法-結束=====");
}
}
1.4.8. 對計算結果的異常處理
當CompletableFuture的計算結果完成,或者拋出異常的時候,可以通過handle方法對結果進行處理。
exceptionally() 方法將導致CompletableFuture 內發生問題的異常拋出。
方法名 | 描述 |
---|---|
public CompletableFuture handle(BiFunction<? super T, Throwable, ? extends U> fn) | 當CompletableFuture的計算結果完成,或者拋出異常的時候,可以通過handle方法對結果進行處理。 |
public CompletableFuture handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) | 當CompletableFuture的計算結果完成,或者拋出異常的時候,可以通過handle方法對結果進行處理。使用ForkJoinPool。 |
public CompletableFuture handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) | 當CompletableFuture的計算結果完成,或者拋出異常的時候,可以通過handle方法對結果進行處理。使用指定的線程池。 |
public CompletableFuture exceptionally(Function<Throwable, ? extends T> fn) | exceptionally() 方法將導致CompletableFuture 內發生問題的異常拋出。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureExample7 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
handle();
exceptionally();
}
/**
handle() 當CompletableFuture的計算結果完成,或者拋出異常的時候,可以通過handle方法對結果進行處理。
執行結果:
=====handle()簽名方法-開始=====
handle()簽名方法,在supplyAsync階段,執行1/0結果。
handle()簽名方法,執行結果:-1
=====handle()簽名方法-結束=====
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1592)
at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.lang.ArithmeticException: / by zero
at com.yuanxw.chapter24.CompletableFutureExample7.lambda$handle$0(CompletableFutureExample3.java:26)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
... 5 more
*/
private static void handle() throws ExecutionException, InterruptedException {
System.out.println("=====handle()簽名方法-開始=====");
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("handle()簽名方法,在supplyAsync階段,執行1/0結果。");
return 1/0;
}).handle((integer, throwable) -> {
if(throwable != null){
throwable.printStackTrace();
return -1;
}
return integer;
});
System.out.println("handle()簽名方法,執行結果:"+ completableFuture.get());
System.out.println("=====handle()簽名方法-結束=====");
}
/**
* exceptionally() 方法將導致CompletableFuture 內發生問題的異常拋出。
* 這樣,當執行任務發生異常時,調用get()方法的線程將會收到一個 ExecutionException 異常,
* 該異常接收了一個包含失敗原因的Exception 參數。
* 執行結果:
=====exceptionally()簽名方法-開始=====
exceptionally()簽名方法,在supplyAsync階段,執行1/0結果。
exceptionally()簽名方法,執行結果:java.lang.ArrayIndexOutOfBoundsException: 1
=====exceptionally()簽名方法-結束=====
* @throws ExecutionException
* @throws InterruptedException
*/
private static void exceptionally() throws ExecutionException, InterruptedException {
System.out.println("=====exceptionally()簽名方法-開始=====");
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println("exceptionally()簽名方法,在supplyAsync階段,執行1/0結果。");
String [] array = new String[]{};
// 此處異常:java.lang.ArrayIndexOutOfBoundsException: 1
return array[1];
}).exceptionally(ex -> ex.getMessage());
System.out.println("exceptionally()簽名方法,執行結果:"+ completableFuture.get());
System.out.println("=====exceptionally()簽名方法-結束=====");
}
}
1.4.9. 其它方法
allOf方法是當所有的CompletableFuture都執行完後執行計算。anyOf方法是當任意一個CompletableFuture執行完後就會執行計算,計算的結果相同。
方法名 | 描述 |
---|---|
public static CompletableFuture allOf(CompletableFuture<?>… cfs) | allOf方法是當所有的CompletableFuture都執行完後執行計算。 |
public static CompletableFuture anyOf(CompletableFuture<?>… cfs) | anyOf方法是當任意一個CompletableFuture執行完後就會執行計算,計算的結果相同。 |
package com.yuanxw.chapter24;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class CompletableFutureExample8 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
anyAndAllOfCompletableFuture();
}
/**
allOf方法是當所有的CompletableFuture都執行完後執行計算。
anyOf方法是當任意一個CompletableFuture執行完後就會執行計算,計算的結果相同。
執行結果:
=====anyAndAllOfCompletableFuture()簽名方法-開始=====
ForkJoinPool.commonPool-worker-1線程,工作-開始>>
ForkJoinPool.commonPool-worker-2線程,工作-開始>>
ForkJoinPool.commonPool-worker-2線程,工作-結束<<
anyOf()簽名方法,執行結果:result-2
ForkJoinPool.commonPool-worker-1線程,工作-結束<<
=====anyAndAllOfCompletableFuture()簽名方法-結束=====
*/
private static void anyAndAllOfCompletableFuture() throws ExecutionException, InterruptedException {
System.out.println("=====anyAndAllOfCompletableFuture()簽名方法-開始=====");
CompletableFuture<String> completableFuture1=CompletableFuture.supplyAsync(()->{
//模擬執行耗時任務
System.out.println(Thread.currentThread().getName() + "線程,工作-開始>>");
// 睡眠5秒
processSleep(5);
System.out.println(Thread.currentThread().getName() + "線程,工作-結束<<");
//返回結果
return "result-1";
});
CompletableFuture<String> completableFuture2=CompletableFuture.supplyAsync(()->{
//模擬執行耗時任務
System.out.println(Thread.currentThread().getName() + "線程,工作-開始>>");
// 睡眠5秒
processSleep(1);
System.out.println(Thread.currentThread().getName() + "線程,工作-結束<<");
//返回結果
return "result-2";
});
CompletableFuture<Object> anyOfCompletableFuture = CompletableFuture.anyOf(completableFuture1,completableFuture2);
System.out.println("anyOf()簽名方法,執行結果:"+ anyOfCompletableFuture.get());
CompletableFuture<Void> allOfCompletableFuture = CompletableFuture.allOf(completableFuture1,completableFuture2);
// 阻塞等待所有任務執行完成
allOfCompletableFuture.join();
System.out.println("=====anyAndAllOfCompletableFuture()簽名方法-結束=====");
}
/**
* 線程休眠的方法
* @param seconds
*/
private static void processSleep(long seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
– 以上爲《JAVA多線程(二十四)Java多線程之CompletableFuture類》,如有不當之處請指出,我後續逐步完善更正,大家共同提高。謝謝大家對我的關注。
——厚積薄發(yuanxw)