文章目錄
1.Future擔當的角色
在介紹Future及其實現類FutureTask之前,我們首先要知道Future是做什麼用的,爲什麼要設計這一系列的接口和實現類。
大多數的併發程序都是圍繞“任務執行”來構造的,任務通常是一些抽象的且離散的工作單元。在設計併發程序時,將線程和任務解耦,可以使程序有更好的性能和伸縮性。舉個簡單的例子,在一個web程序中,可以將每一個用戶請求當做是一個任務,每個請求根據不同的任務執行策略處理。
Future可以封裝各種抽象的任務單元,不僅如此,它提供獲取任務執行結果的接口get()
和其他非常實用的接口方法,比如取消任務,限時任務等,
2. FutureTask使用場景
FutureTask是一個可以返回任務執行結果的任務單元,通常在需要任務執行結果的場景中使用。
- 需要計算並且要獲得計算值的場景
- 在搜索引擎中,在規定時間內獲取搜索的結果,並將搜索結果返回給用戶。(超時的任務將取消)
3. Callable、Future和FutureTask
Callabe接口
public interface Callable<V> {
V call() throws Exception;
}
Callable接口比較簡單,只有一個方法,用來返回任務執行結果。
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;
}
-
cancel(boolean mayInterruptIfRunning)
取消當前任務的執行。- 如果執行
cancel
時任務已完成或者之前已進行取消操作或者因爲某些原因不能進行取消操作,那麼將返回false
- 如果執行
cancel
時線程還未執行該任務,那麼該任務將不會被執行,返回ture
- 如果執行
cancel
時線程正在處理該任務,且mayInterruptIfRunning
爲false
,那麼任務會繼續執行到執行完成,此時返回ture
- 如果執行
cancel
時線程正在處理該任務,且mayInterruptIfRunning
爲ture
,那麼會中斷該線程,此時返回ture
- 如果執行
-
isCancelled()
獲取任務是否被取消。- 如果任務在取消前正常完成,那麼返回
ture
- 如果任務在取消前正常完成,那麼返回
-
isDone()
獲取任務是否已完成- 如果任務已完成,返回
true
。如果任務時因中斷,異常等原因被終止,也返回true
- 如果任務已完成,返回
-
get()
獲取任務執行結果,get()
方法會一直阻塞直到任務完成。如果任務被中斷,將拋出InterruptedException
-
get(long timeout, TimeUnit unit)
在規定時間內獲取任務執行結果,如果沒有在規定時間內完成任務則拋出TimeoutException
FutureTask類
以下爲FutureTask的類圖
FutureTask是Future接口的實現類也是唯一的實現類
,FutureTask實現了RunnableFuture,即同時實現了Future接口及Runnable接口,可以很方便的在線程中被執行。
FutureTask實現了Future和Runnable的接口,這裏不再贅述。
不過有一點需要注意的是FutureTask的構造方法和實現run()的方法
public class FutureTask<V> implements RunnableFuture<V> {
private Callable<V> callable; //任務執行的主體
private volatile int state; //任務執行的狀態
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
public run(){
/**省略部分代碼**/
V result = callable.call();
/**省略部分代碼**/
}
}
可以看到在FutureTask中,真正執行的方法是其成員變量Callable,這也是爲什麼FutureTask和Callable一起被使用的原因
4.FutureTask使用例子
簡單的使用
這裏定義一個簡單的計算任務,將該任務放入一個新建立的線程中運行,然後調用get
方法獲取計算的結果並輸出。
public class useFutureTask(){
public static void main(String[] args){
//建立一個簡單的計算任務
Future<Integer> future =new FutureTask<>(()->{
Integer a=100;
Integer b=1000;
return a+b;
});
//將該任務放入線程中運行
new Thread((FutureTask)future).start();
//獲取任務計算結果
System.out.println(future.get());)
}
}
輸出:1100
將任務放入線程池中執行
public class useFutureTask(){
public static void main(String[] args){
//建立一個簡單的計算任務
Future<Integer> future =new FutureTask<>(()->{
Integer a=100;
Integer b=1000;
return a+b;
});
//新建一個帶緩存的線程池,並將任務放入線程池中執行
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit((FutureTask)future);
//獲取任務計算結果
System.out.println(future.get());
//記得關閉線程池,不然jvm虛擬機不會退出
executor.shutdown();
}
}
輸出:1100