java線程池的基本使用

四種線程池

Executors中提供了四種線程池:segmentfault

  1. newCachedThreadPool 可緩存線程池,對於每一個線程,若是有空閒線程可用,當即讓它執行,若是沒有,則建立一個新線程
  2. newFixedThreadPool 具備固定大小的線程池,若是任務數大於空閒的線程數,則把它們放進隊列中等待
  3. newSingleThreadPool大小爲1的線程池,任務一個接着一個完成
  4. newScheduledThreadPool 定長線程池,可控制線程最大併發數,支持定時及週期性任務執行,用來代替Timer

基本方法

在上文http://segmentfault.com/a/1190000003091174 中說到了callable不能直接被Thread運行,但卻能被線程池運行,ExecutorService提供了幾種方法運行一個任務:緩存

Future submit(Callable task);
Future submit(Runnable task, T result);
Future submit(Runnable task);併發

第一個方法能夠直接提交一個Callable任務,返回一個包含結果的Future<T>,第二個方法會返回指定的result對象,第三個方法返回一個Future<?>,可使用這樣的對象來調用isDone,cancel,isCancelled,可是在get的時候返回null。線程

此外,有兩個經常使用的關閉線程池的方法:code

void shutdown();
List<Runnable> shutdownNow()對象

第一個方法將啓動一次順序關閉,有任務在執行,則等待執行完成,但不接受新的任務;
第二個方法將取消全部未開始的任務而且試圖中斷正在執行的任務,返回從未開始執行的任務的列表。沒法保證可以中止正在處理的活動執行任務,可是會盡力嘗試。例如,經過 Thread.interrupt() 來取消典型的實現,因此任何任務沒法響應中斷均可能永遠沒法終止。隊列

控制一組任務

ExecutorService提供了invokeAnyinvokeAll方法,它們是批量執行的最經常使用形式,它們執行任務collection,而後等待至少一個,或所有任務完成get

/**
執行給定的任務,當全部任務完成時,返回保持任務狀態和結果的 Future 列表。返回列表的全部元素的 Future.isDone() 爲 true。
注意,能夠正常地或經過拋出異常來終止已完成任務。
**/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
                          throws InterruptedException
/**
執行給定的任務,若是其中一個任務的結果。一旦正常或異常返回後,則取消還沒有完成的任務。
**/                          
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
            throws InterruptedException,
                   ExecutionException

invokeAny方法提交全部任務到一個Callable對象的集合中,而且返回某個已經完成了的任務的結果,返回的任務是不肯定的。invokeAll方法則返回全部任務的結果,能夠這樣來對結果進行處理:it

List<Callable<T>> tasks=...
List<Future<T>> results = executor.invokeAll(tasks);
for(Future<T> result : results){
    process(result.get());
}
...

這樣處理的一個弊端是,若是第一個任務花費了很長時間,則不得不等待。在某些狀況下,可能只須要一個任務出告終果就能夠停止全部任務,這樣就得不償失。將結果按照可得到的順序保存起來可能更好,這時須要用到ExecutorCompletionService來進行排列:io

ExecutorCompletionService service = new ExecutorCompletionService(executor);
for(Callable<T> task:tasks){
    service.submit(task);
}

for(int i = 0;i < task.size();i++){
    process(service.take().get());
}
...

其中,take()方法會移除下一個已經完成的結果(Future),若是沒有可用結果則阻塞

使用小結

在使用線程池時,大多應該按照如下步驟:

  1. 調用Executors類中的靜態方法newCachedThreadPoolnewFixedThreadPool建立線程池;
  2. 調用submit提交RunnableCallable任務;
  3. 若是想取消一個任務,或者提交了Callable對象,那就要保存好返回的Future對象;
  4. 當再也不提交任務時,調用shutdown
相關文章
相關標籤/搜索