Future和Callable
Future和Callable
Runnable缺陷
- 不能返回一個返回值
- 也不能拋出checked Exception
Callable接口
- 類似于Runnable,被其它線程執行的任務
- 實現call方法
- 有返回值
Future
- 可以用Future.get來獲取Callable接口返回的執行結果,還可以通過Future.isDone()來判斷任務是否已經執行完了,以及取消這個任務,限時獲取任務的結果等
- 在call()未執行完畢之前,調用get()的線程(假定此時是主線程)會被阻塞,直到call()方法返回了結果后,此時future.get()才會得到該結果,然后主線程才會切換到runnable狀態
- 所以Future是一個存儲器,它存儲了call()這個任務的結果,而這個任務的執行時間是無法提前確定的,因為這完全取決于call()方法執行的情況
get():獲取結果
get方法的行為取決于Callable任務的狀態,只有以下這5種情況:
1.任務正常完成:get方法會立刻返回結果
2.任務尚未完成(任務還沒開始或進行中):get將阻塞并直到任務完成
3.任務執行過程中get方法會拋出ExecutionException這里的拋出異常,是call(執行時產生的那個異常,看到這個異常類型是java.util.concurrent.ExecutionException。不論call()執行時拋出的異常類型是什么,最后get方法拋出的異常類型都是ExecutionException。
4.任務被取消:get方法會拋出CancellationException
5.任務超時:get方法有一個重載方法,是傳入一個延遲時間的如果時間到了還沒有獲得結果,get方法就會拋出TimeoutException。
用get(long timeout,TimeUnit unit)方法時,如果call()在規定時間內完成了任務,那么就會正常獲取到返回值;而如果再指定時間內沒有計算出結果,那么就會拋出TimeoutException
cancel()方法:取消任務
isDone()方法:判斷線程是否執行完畢
isCanncelled()方法:判斷是否被取消
線程池的submit方法返回Future對象
首先,我們要給線程池提交我們的任務,提交時線程池會立刻返回給我們一個空的Future容器。當線程的任務一旦執行完畢也就是當我們可以獲取結果的時候,線程池便會把該結果填入到之前給我們的那個Future中去(而不是創建-個新的Future),我們此時便可以從該Future中獲得任務執行的結果
public class OneFuture {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
}
Futrue的Lambda
public class OneFutureLambda {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Callable<Integer> callable = () -> {
Thread.sleep(3000);
return new Random().nextInt();
};
Future<Integer> future = service.submit(callable);
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}
批量接收結果
多個任務,用Futrue數組來獲取結果
public class MultiFutures {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(20);
ArrayList<Future> futures = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Future<Integer> future = service.submit(new CallableTask());
futures.add(future);
}
Thread.sleep(5000);
for (int i = 0; i < 20; i++) {
Future<Integer> future = futures.get(i);
try {
Integer integer = future.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
}
執行時異常和isDone()
/**
* 描述: 演示get方法過程中拋出異常,for循環為了演示拋出Exception的時機:并不是說一產生異常就拋出,直到我們get執行時,才會拋出。
*/
public class GetException {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(20);
Future<Integer> future = service.submit(new CallableTask());
try {
for (int i = 0; i < 5; i++) {
System.out.println(i);
Thread.sleep(500);
}
System.out.println(future.isDone());
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("InterruptedException異常");
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println("ExecutionException異常");
}
}
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
throw new IllegalArgumentException("Callable拋出異常");
}
}
}
獲取任務超時
/**
* 描述: 演示get的超時方法,需要注意超時后處理,調用future.cancel()。演示cancel傳入true和false的區別,代表是否中斷正在執行的任務。
*/
public class Timeout {
private static final Ad DEFAULT_AD = new Ad("無網絡時候的默認廣告");
private static final ExecutorService exec = Executors.newFixedThreadPool(10);
static class Ad {
String name;
public Ad(String name) {
this.name = name;
}
@Override
public String toString() {
return "Ad{" +
"name='" + name + '\'' +
'}';
}
}
static class FetchAdTask implements Callable<Ad> {
@Override
public Ad call() throws Exception {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("sleep期間被中斷了");
return new Ad("被中斷時候的默認廣告");
}
return new Ad("旅游訂票哪家強?找某程");
}
}
public void printAd() {
Future<Ad> f = exec.submit(new FetchAdTask());
Ad ad;
try {
ad = f.get(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
ad = new Ad("被中斷時候的默認廣告");
} catch (ExecutionException e) {
ad = new Ad("異常時候的默認廣告");
} catch (TimeoutException e) {
ad = new Ad("超時時候的默認廣告");
System.out.println("超時,未獲取到廣告");
boolean cancel = f.cancel(true);
System.out.println("cancel的結果:" + cancel);
}
exec.shutdown();
System.out.println(ad);
}
public static void main(String[] args) {
Timeout timeout = new Timeout();
timeout.printAd();
}
}
取消任務cancel
1.如果這個任務還沒有開始執行,那么這種情兄最簡單,任務會被正常的取消,未來也不會被執行,方法返回true。
2.如果任務已完成,或者已取消:那么cancel()方法會執行失敗方法返回false。
3.如果這個任務已經開始執行了,那么這個取消方法將不會直接取消該任務,而是會根據我們填的參數mayInterruptIfRunning做判斷
Future.cancel(true)適用于:
1.任務能夠處理interrupt
Future.cancel(false)僅用于避免啟動尚未啟動的任務,適用于:
1.未能處理interrupt的任務
2.不清楚任務是否支持取消
3.需要等待已經開始的任務執行完成
FutureTask來獲取Future和任務結果
- FutureTask是一種包裝器,可以把Callable轉化成Future和Runnable,它同時實現二者的接口

- 把Callable實例當作參數,生成FutureTask的對象,然后把這個對象當作一個Runnable對象,用線程池或另起線程去執行這個Runnable對象,最后通過FutureTask獲取剛才執行的結果
/**
* 描述: 演示FutureTask的用法
*/
public class FutureTaskDemo {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
// new Thread(integerFutureTask).start();
ExecutorService service = Executors.newCachedThreadPool();
service.submit(integerFutureTask);
try {
System.out.println("task運行結果:"+integerFutureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子線程正在計算");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
Future注意點
- 當for循環批量獲取future的結果時,容易發生一部分線程很慢的情況,get方法調用時應使用timeout限制
- Future的生命周期不能后退

浙公網安備 33010602011771號