天天看點

java 多線程執行不同的運算_JAVA筆記_多線程_異步計算

一、 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());