Future
Future接口是Java多線程Future模式的實現,在java.util.concurrent包中,可以用來進行異步計算。
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
一般情況下,我們會結合Callable和Future一起使用,通過ExecutorService的submit方法執行Callable,並返回Future。
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(() -> { //Lambda 是一個 callable, 提交後便立即執行,這裏返回的是 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 的執行結果,執行完畢之後打印出來
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} finally {
executor.shutdown();
}
比起future.get(),其實更推薦使用get (long timeout, TimeUnit unit) 方法,設置了超時時間可以防止程序無限制的等待future的結果。
Future模式的缺點
- Future雖然可以實現獲取異步執行結果的需求,但是它沒有提供通知的機制,我們無法得知Future什麼時候執行完成
- 要麼使用阻塞,在future.get()方法的地方等待future返回的結果,這時又變成了同步操作。要麼使用isDone()輪詢判斷Future是否完成。單這樣會耗費CPU資源(CPU空轉),於是completableFuture應運而生。
CompletableFuture
從Java 8開始引入了CompletableFuture,它針對Future做了改進,可以傳入回調對象,當異步任務完成或者發生異常時,自動調用回調對象的回調方法。
我們以異步調用無返回值爲例,看看如何使用CompletableFuture:
package com;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @className:
* @author: youjp
* @create: 2020-06-21 17:19
* @description: 異步回調,completableFuture,對將來的結果進行結果,ajax請求就是一種異步回調!
* 1.異步回調
* 2.成功回調
* 3.失敗回調
* @Version: 1.0
*/
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//無返回值的異步回調測試
CompletableFuture<Void> completableFuture=CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":沒有返回結果的回調");
});
System.out.println(111);
System.out.println(completableFuture.get()); //獲取阻塞執行結果:即沒有獲取到執行結果前,程序一直阻塞執行
}
}
可以查看到,因爲是異步執行的,所以在爲獲取結果前,先執行了主線程。然後再獲取結果的同時阻塞去獲取執行結果。
CompletableFuture有返回結果情況測試
package com;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @className:
* @author: youjp
* @create: 2020-06-21 17:19
* @description: 異步回調,completableFuture,對將來的結果進行結果,ajax請求就是一種異步回調!
* 1.異步回調
* 2.成功回調
* 3.失敗回調
* @Version: 1.0
*/
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//有返回值的supplyAsync 異步回調
//在ajax請求中,有成功和失敗的回調,成功即返回成功的數據,錯誤就返回錯誤的數據
CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+":異步回調,返回integer");
return 200;
});
//回調成功即 success
System.out.println("異步調用返回結果:"+completableFuture.whenComplete((x,y)->{
System.out.println("x-即success成功返回的結果:"+x); //正常返回結果
System.out.println("y-即error錯誤返回的:"+y); //錯誤返回結果
}).exceptionally(e->{
System.out.println("獲取異常信息"+e.getMessage());
return 500; //返回異常結果
}).get());
}
}
調用成功,則獲取到返回的值
設立一個程序算法異常,查看異常回調的結果
package com;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @className:
* @author: youjp
* @create: 2020-06-21 17:19
* @description: 異步回調,completableFuture,對將來的結果進行結果,ajax請求就是一種異步回調!
* 1.異步回調
* 2.成功回調
* 3.失敗回調
* @Version: 1.0
*/
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//有返回值的supplyAsync 異步回調
//在ajax請求中,有成功和失敗的回調,成功即返回成功的數據,錯誤就返回錯誤的數據
CompletableFuture<Integer> completableFuture=CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+":異步回調,返回integer");
int num=400/0;
return 200;
});
//回調成功即 success
System.out.println("異步調用返回結果:"+completableFuture.whenComplete((x,y)->{
System.out.println("x-即success成功返回的結果:"+x); //正常返回結果
System.out.println("y-即error錯誤返回的:"+y); //錯誤返回結果
}).exceptionally(e->{
System.out.println("獲取異常信息"+e.getMessage());
return 500; //返回異常結果
}).get());
}
}