【Java執行緒】Callable和Future
Future模式
Future介面是Java執行緒Future模式的實現,可以來進行非同步計算。
Future模式可以這樣來描述:
我有一個任務,提交給了Future,Future替我完成這個任務。期間我自己可以去做任何想做的事情。一段時間之後,我就便可以從Future那兒取出結果。
就相當於下了一張訂貨單,一段時間後可以拿著提訂單來提貨,這期間可以幹別的任何事情。其中Future介面就是訂貨單,真正處理訂單的是Executor類,它根據Future介面的要求來生產產品。
Callable和Future介面
Callable介面
Callable和Future一個產生結果,一個拿到結果。
Callable
- V call()
/** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception;
Future介面
Future 表示非同步計算的結果。Future介面中有如下方法:
- boolean cancel(boolean mayInterruptIfRunning)
取消任務的執行。引數指定是否立即中斷任務執行,或者等等任務結束
- boolean isCancelled()
任務是否已經取消,任務正常完成前將其取消,則返回 true
- boolean isDone()
任務是否已經完成。需要注意的是如果任務正常終止、異常或取消,都將返回true
- V get()
等待任務執行結束,然後獲得V型別的結果。InterruptedException 執行緒被中斷異常, ExecutionException任務執行異常,如果任務被取消,還會丟擲CancellationException
- V get(long timeout, TimeUnit unit)
同上面的get功能一樣,多了設定超時時間。引數timeout指定超時時間,uint指定時間的單位,在列舉類TimeUnit中有相關的定義。如果計算超時,將丟擲TimeoutException
Future介面提供方法來檢測任務是否被執行完,等待任務執行完獲得結果。也可以設定任務執行的超時時間,這個設定超時的方法就是實現Java程式執行超時的關鍵。
所以,如果需要設定程式碼執行的最長時間,即超時,可以用Java執行緒池ExecutorService類配合Future介面來實現。
int result = future.get(5000, TimeUnit.MILLISECONDS);
Future實現類:SwingWorker
http://blog.csdn.net/vking_wang/article/details/8994882Future實現類:FutureTask
Future的實現類有java.util.concurrent.FutureTask<V>即 javax.swing.SwingWorker<T,V>。通常使用FutureTask來處理我們的任務。
FutureTask類同時又實現了Runnable介面,所以可以直接提交給Thread、Executor執行。
public class CallableAndFuture {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(5000);// 可能做一些事情
int result = future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
通過ExecutorService的submit方法執行Callable,並返回Future
使用ExecutorService:
public class CallableAndFuture {
public static void main(String[] args) {
//ExecutorService.submit()
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<Integer> future = threadPool.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
});
try {
Thread.sleep(5000);// 可能做一些事情
int result = future.get()); //Future.get()
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
如果要執行多個帶返回值的任務,並取得多個返回值,可用CompletionService:
CompletionService相當於Executor加上BlockingQueue,使用場景為當子執行緒併發了一系列的任務以後,主執行緒需要實時地取回子執行緒任務的返回值並同時順序地處理這些返回值,誰先返回就先處理誰。
public class CallableAndFuture {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
for(int i = 1; i < 5; i++) {
final int taskID = i;
//CompletionService.submit()
cs.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return taskID;
}
});
}
// 可能做一些事情
for(int i = 1; i < 5; i++) {
try {
int result = cs.take().get()); //CompletionService.take()返回Future
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
或者不使用CompletionService:先建立一個裝Future型別的集合,用Executor提交的任務返回值新增到集合中,最後便利集合取出資料。
區別:
Future集合方法,submit的task不一定是按照加入自己維護的list順序完成的。從list中遍歷的每個Future物件並不一定處於完成狀態,這時呼叫get()方法就會被阻塞住,如果系統是設計成每個執行緒完成後就能根據其結果繼續做後面的事,這樣對於處於list後面的但是先完成的執行緒就會增加了額外的等待時間。
而CompletionService的實現是維護一個儲存Future物件的BlockingQueue。只有當這個Future物件狀態是結束的時候,才會加入到這個Queue中,take()方法其實就是Producer-Consumer中的Consumer。它會從Queue中取出Future物件,如果Queue是空的,就會阻塞在那裡,直到有完成的Future物件加入到Queue中。
所以,先完成的必定先被取出。這樣就減少了不必要的等待時間。