一、 Callable,Future,FutureTask
Callable接口與Runnable接口類似,封裝了一個異步運作的任務,但與run方法不同,它的call方法可以有一個傳回值并且可以抛出異常。Callable接口是個參數化的類型 public interface Callable{ V call() throws Exception} ,類型參數就是傳回值的類型。
Future接口用于表示異步計算的結果,并提供了檢查計算是否完成的方法,以等待計算的完成,并擷取計算的結果。計算完成後隻能使用get方法來擷取結果,如有必要,計算完成前可以阻塞此方法。取消則由cancel方法來執行。還提供了其他方法,以确定任務是正常完成還是被取消了。一旦計算完成,就不能再取消計算。如果為了可取消性而使用Future但又不提供可用的結果,則可以聲明Future>形式類型、并傳回null作為底層任務的結果。下面是JDK給的例子:
interface ArchiveSearcher { String search(String target); }
class App {
ExecutorService executor = ...
ArchiveSearcher searcher = ...
void showSearch(final String target)throws InterruptedException {
Futurefuture = executor.submit(new Callable() {
public String call() {
return searcher.search(target);
}});
displayOtherThings(); // do other things while searching
try {
displayText(future.get()); // use future
} catch (ExecutionException ex) { cleanup(); return; }
}
}
FutureTask類代表可取消的異步計算,它采用包裝器機制,可将Callable轉換成Runnable和Future,它同時實作了這兩個接口。下面是用法執行個體:
CallablemyCallable = new MyCallable();
FutureTask task = new FutureTask(myCallable);
new Thread(task).start();
...
T result = task.get();
二、執行器
1.Executor接口與ThreadPoolExecutor
Executor用于執行已送出的Runnable任務,它将任務的送出與每個任務如何運作的機制(包括線程使用的細節、排程等)分離開來。它僅提供了一個excecute方法。它有兩個子接口:ExcecuteService和SheduledExecutorService
ExecutorService提供了相關方法以管理終止,以及相關方法以跟蹤一個或多個異步任務執行狀況。可以關閉ExecutorService,shutdown方法被調用後将等待已送出的任務運作結束才會關閉服務,而shutdownNow方法将阻止任務啟動并嘗試停止正在運作的任務。被關閉的ExecutorService沒有等待啟動的任務,沒有正在運作的任務,也無法向其送出新的任務。應該關閉不再使用的ExecutorService以回收資源。
ThreadPoolExecutor線程池實作了ExecutorService接口。使用線程池可以避免頻繁建立線程的開銷,适合執行大量生命周期較短的任務。對于每個任務,如果線程池中有線程可用,則讓它立即執行任務,否則建立一個新線程讓其執行或者将其放入等待隊列。
線程池的常見用法:
(1)調用Executors中的靜态方法或者ThreadPoolExecutor構造方法建立線程池對象。
(2)調用submit送出Runnable或Callable對象,
(3)如果要取消一個任務,那就應該儲存好傳回的Future對象。
(4)當不再送出任何任務時,應該調用shutdown方法關閉線程池
2. 控制任務組
ExecutorService執行器提供了許多控制任務執行的方法。
shutdownNow方法可以取消所有正在執行的任務和未執行的任務。
invokeAny方法将所有所有任務對象作為一個集合送出到執行器中,并傳回一個Future清單。當這些任務中的任一個執行完畢後傳回結果,無法知道傳回的究竟是哪個任務的結果。此方法适合于多種方式并行處理一個問題的場景,隻要求盡快得到結果,具體哪種方法起作用不關心。
invokeAll與invokeAny不同,所有任務都會傳回結果。但如果Future清單中第一個對應任務執行時間最長,則其他任務結果的獲得不得不等待。可以使用ExecutorCompletionService來擷取順序傳回的任務結果。
ExecutorCompletionService的用法:
ExecutorCompletionService service = newExecutorCompletionService(executor);
for(Callabletask : tasks)
service.submit(task);
for(int i=0;i
processFurther(service.take().get());