Java8新的異步編程方式 CompletableFuture
緣起:
一、Future
java5引入了Future模式。Future接口是Java多線程Future模式的實現,在java.util.concurrent包中,可以來進行異步計算。
Future模式是多線程設計常用的一種設計模式。Future模式可以理解成:我有一個任務,提交給了Future,Future替我完成這個任務。期間我自己可以去做任何想做的事情。一段時間之後,我就便可以從Future那兒取出結果。
Future的接口的五個方法。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future接口的方法介紹如下:
boolean cancel (boolean mayInterruptIfRunning) 取消任務的執行。參數指定是否立即中斷任務執行,或者等等任務結束
boolean isCancelled () 任務是否已經取消,任務正常完成前將其取消,則返回 true
boolean isDone () 任務是否已經完成。需要注意的是如果任務正常終止、異常或取消,都將返回true
V get () throws InterruptedException, ExecutionException 等待任務執行結束,然後獲得V類型的結果。InterruptedException 線程被中斷異常, ExecutionException任務執行異常,如果任務被取消,還會拋出CancellationException
V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一樣,多了設置超時時間。參數timeout指定超時時間,uint指定時間的單位,在枚舉類TimeUnit中有相關的定義。如果計 算超時,將拋出TimeoutException
Future的侷限性
Future雖然可以實現獲取異步執行結果的需求,但是它沒有提供通知的機制,我們無法得知Future什麼時候完成。
要麼使用阻塞,在future.get()的地方等待future返回的結果,這時又變成同步操作。要麼使用isDone()輪詢地判斷Future是否完成,這樣會耗費CPU的資源。
二、CompletableFuture介紹
Java 8新增的CompletableFuture類正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特徵,還提供了其它強大的功能,讓Java擁有了完整的非阻塞編程模型:Future、Promise 和 Callback(在Java8之前,只有無Callback 的Future)。
CompletableFuture能夠將回調放到與任務不同的線程中執行,也能將回調作爲繼續執行的同步函數,在與任務相同的線程中執行。它避免了傳統回調最大的問題,那就是能夠將控制流分離到不同的事件處理器中。
CompletableFuture彌補了Future模式的缺點。在異步的任務完成後,需要用其結果繼續操作時,無需等待。可以直接通過thenAccept、thenApply、thenCompose等方式將前面異步處理的結果交給另外一個異步事件處理線程來處理。
CompletableFuture中的靜態方法
方法名 描述
runAsync(Runnable runnable) 使用ForkJoinPool.commonPool()作爲它的線程池執行異步代碼。異步操作無返回值
runAsync(Runnable runnable, Executor executor) 使用指定的thread pool執行異步代碼。使用指定線程池
supplyAsync(Supplier<U> supplier) 使用ForkJoinPool.commonPool()作爲它的線程池執行異步代碼,異步操作有返回值
supplyAsync(Supplier<U> supplier, Executor executor) 使用指定的thread pool執行異步代碼,異步操作有返回值
獲取結果
public T get()
public T get(long timeout, TimeUnit unit) //設置超時時間
public T getNow(T valueIfAbsent) //如果結果當前已經計算完則返回結果或者拋出異常,否則返回給定的valueIfAbsent值,不會阻塞
public T join() //join()返回計算的結果或者拋出一個unchecked異常(CompletionException)
對多個異步任務進行流水線操作
當CompletableFuture的計算結果完成,或者拋出異常的時候,我們可以執行特定的操作
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action) //和之前的CompleteableFuture使用相同的線程
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action) //新選線程
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor) //指定線程池
public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn) //exceptionally方法返回一個新的CompletableFuture,當原始的 CompletableFuture拋出異常的時候,就會觸發這個CompletableFuture的計算
三、實例
1、簡單異步,獲取返回值
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2021/6/10 11:20
**/
public class FutureDemo {
public static void main(String[] args) {
try {
String s = CompletableFuture.supplyAsync(() -> {
return "hello";
}).get();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
2、等待線程
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2021/6/10 11:25
**/
public class ComletableFutureDemo {
private static Random rand = new Random();
private static long t = System.currentTimeMillis();
public static void main(String[] args) {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()->{
System.out.println("begin to start compute");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("end to start compute. passed " + (System.currentTimeMillis() - t)/1000 + " seconds");
return rand.nextInt(1000);
});
Future<Integer> f = future.whenComplete((v, e)->{
System.out.println(v);
System.out.println(e);
});
try {
System.out.println(f.get());
System.in.read();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
}
3、這一組方法接受一個Function作爲參數,這個Function的輸入是當前的CompletableFuture的計算值,返回結果將是一個新的CompletableFuture,這個新的CompletableFuture會組合原來的CompletableFuture和函數返回的CompletableFuture
/**
* @Author: Liu Yue
* @Descripition:
* @Date; Create in 2021/6/10 11:52
**/
public class ComletableFutureDemo2 {
public static void main(String[] args) {
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
return 100;
});
CompletableFuture<String> future =
integerCompletableFuture.thenCompose(i->{
return CompletableFuture.supplyAsync(() -> {
return (i * 10) + "";
});
});
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}