一. Future
JDK 5引入了Future模式谚殊。Future接口是Java多線程Future模式的實現(xiàn),在java.util.concurrent包中碑隆,可以來進行異步計算赠法。
Future模式是多線程設(shè)計常用的一種設(shè)計模式对雪。Future模式可以理解成:我有一個任務(wù)怜庸,提交給了Future当犯,F(xiàn)uture替我完成這個任務(wù)。期間我自己可以去做任何想做的事情割疾。一段時間之后嚎卫,我就便可以從Future那兒取出結(jié)果。
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) 取消任務(wù)的執(zhí)行。參數(shù)指定是否立即中斷任務(wù)執(zhí)行麻昼,或者等等任務(wù)結(jié)束
- boolean isCancelled () 任務(wù)是否已經(jīng)取消奠支,任務(wù)正常完成前將其取消,則返回 true
- boolean isDone () 任務(wù)是否已經(jīng)完成抚芦。需要注意的是如果任務(wù)正常終止胚宦、異常或取消燕垃,都將返回true
- V get () throws InterruptedException, ExecutionException 等待任務(wù)執(zhí)行結(jié)束枢劝,然后獲得V類型的結(jié)果。InterruptedException 線程被中斷異常卜壕, ExecutionException任務(wù)執(zhí)行異常您旁,如果任務(wù)被取消,還會拋出CancellationException
- V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一樣轴捎,多了設(shè)置超時時間鹤盒。參數(shù)timeout指定超時時間,uint指定時間的單位侦副,在枚舉類TimeUnit中有相關(guān)的定義侦锯。如果計 算超時,將拋出TimeoutException
一般情況下秦驯,我們會結(jié)合Callable和Future一起使用尺碰,通過ExecutorService的submit方法執(zhí)行Callable,并返回Future译隘。
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(() -> { //Lambda 是一個 callable亲桥, 提交后便立即執(zhí)行,這里返回的是 FutureTask 實例
System.out.println("running task");
Thread.sleep(10000);
return "return task";
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("do something else"); //前面的的 Callable 在其他線程中運行著固耘,可以做一些其他的事情
try {
System.out.println(future.get()); //等待 future 的執(zhí)行結(jié)果题篷,執(zhí)行完畢之后打印出來
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} finally {
executor.shutdown();
}
比起future.get(),其實更推薦使用get (long timeout, TimeUnit unit) 方法厅目,設(shè)置了超時時間可以防止程序無限制的等待future的結(jié)果番枚。
二. CompletableFuture介紹
2.1 Future模式的缺點
Future雖然可以實現(xiàn)獲取異步執(zhí)行結(jié)果的需求法严,但是它沒有提供通知的機制,我們無法得知Future什么時候完成葫笼。
要么使用阻塞深啤,在future.get()的地方等待future返回的結(jié)果,這時又變成同步操作渔欢。要么使用isDone()輪詢地判斷Future是否完成,這樣會耗費CPU的資源瘟忱。
2.2 CompletableFuture介紹
Netty奥额、Guava分別擴展了Java 的 Future 接口,方便異步編程访诱。
Java 8新增的CompletableFuture類正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特征垫挨,還提供了其它強大的功能,讓Java擁有了完整的非阻塞編程模型:Future触菜、Promise 和 Callback(在Java8之前九榔,只有無Callback 的Future)。
CompletableFuture能夠?qū)⒒卣{(diào)放到與任務(wù)不同的線程中執(zhí)行涡相,也能將回調(diào)作為繼續(xù)執(zhí)行的同步函數(shù)哲泊,在與任務(wù)相同的線程中執(zhí)行。它避免了傳統(tǒng)回調(diào)最大的問題催蝗,那就是能夠?qū)⒖刂屏鞣蛛x到不同的事件處理器中切威。
CompletableFuture彌補了Future模式的缺點。在異步的任務(wù)完成后丙号,需要用其結(jié)果繼續(xù)操作時先朦,無需等待∪В可以直接通過thenAccept喳魏、thenApply、thenCompose等方式將前面異步處理的結(jié)果交給另外一個異步事件處理線程來處理怀薛。
三. CompletableFuture特性
3.1 CompletableFuture的靜態(tài)工廠方法
方法名 | 描述 |
---|---|
runAsync(Runnable runnable) | 使用ForkJoinPool.commonPool()作為它的線程池執(zhí)行異步代碼刺彩。 |
runAsync(Runnable runnable, Executor executor) | 使用指定的thread pool執(zhí)行異步代碼。 |
supplyAsync(Supplier<U> supplier) | 使用ForkJoinPool.commonPool()作為它的線程池執(zhí)行異步代碼枝恋,異步操作有返回值 |
supplyAsync(Supplier<U> supplier, Executor executor) | 使用指定的thread pool執(zhí)行異步代碼迂苛,異步操作有返回值 |
runAsync 和 supplyAsync 方法的區(qū)別是runAsync返回的CompletableFuture是沒有返回值的。
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Hello");
});
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("CompletableFuture");
而supplyAsync返回的CompletableFuture是由返回值的鼓择,下面的代碼打印了future的返回值三幻。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("CompletableFuture");
3.2 Completable
方法名 | 描述 |
---|---|
complete(T t) | 完成異步執(zhí)行,并返回future的結(jié)果 |
completeExceptionally(Throwable ex) | 異步執(zhí)行不正常的結(jié)束 |
future.get()在等待執(zhí)行結(jié)果時呐能,程序會一直block念搬,如果此時調(diào)用complete(T t)會立即執(zhí)行抑堡。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.complete("World");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
執(zhí)行結(jié)果:
World
可以看到future調(diào)用complete(T t)會立即執(zhí)行。但是complete(T t)只能調(diào)用一次朗徊,后續(xù)的重復(fù)調(diào)用會失效首妖。
如果future已經(jīng)執(zhí)行完畢能夠返回結(jié)果,此時再調(diào)用complete(T t)則會無效爷恳。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete("World");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
執(zhí)行結(jié)果:
Hello
如果使用completeExceptionally(Throwable ex)則拋出一個異常有缆,而不是一個成功的結(jié)果。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.completeExceptionally(new Exception());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
執(zhí)行結(jié)果:
java.util.concurrent.ExecutionException: java.lang.Exception
...