Java併發編程藝術 9 Java中的線程池

第9章  Java中的線程池

線程池的優點
【1】降低資源消耗。通過重複利用已創建的線程,可以減少創建、銷燬線程的消耗
【2】提高響應速度。可以直接使用已創建線程。
【3】提高線程的管理性。通過線程池統一的分配、調優和監控。

線程池創建
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue) 最少參數
     {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
     }

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)最多參數


int corePoolSize 線程池的基本的大小,當提交一個任務到線程池,線程池會創建一個線程來執行任務。即使有空閒線程也會創建新的線程。線程數到達corePoolSize大小時,就不在創建新的。如果使用線程池的prestartAllCoreThreads()方法,線程池會提前創建並啓動所有基本線程。
int maximumPoolSize 線程池最大數量,線程池允許創建的最大數量。如果任務隊列已經滿了,並且已創建的線程數小於maximumPoolSize,線程池會再創建新的線程。如果阻塞隊列是無界隊列,這個參數就沒軟用了。
long keepAliveTime 線程保持活動時間,工作線程空閒後保持的存活時間。
TimeUnit unit      線程保持活動時間的單位。可選DAYS天、HOURS小時、MINUTES分鐘、MILLISECOUNDS毫秒
BlockingQueue<Runnable> workQueue  用於保存等待執行的任務的阻塞隊列。可以選擇ArrayBlockingQueue、LinkedBlockingQueue等
ThreadFactory threadFactory  用於設置創建線程的工廠,可以通過線程工廠爲每個線程設置有意義的名字。使用了開源框架的guava提供的ThreadFactoryBuilder。
RejectedExecutionHandler handler  飽和策略。當隊列和線程池飽和的情況下,採用的提交策略。默認是AbortPolicy



==========================================================================================================================




 ---------- 




提交任務:submit和execute

可以通過submit()和execute()方法提交任務。
區別:
【1】execute()是在Executor接口中定義,也是Executor中唯一的抽象方法。submit()是在ExecutorService中定義的(繼承了Executor接口)
【2】execute()方法沒有返回值,submit()返回一個Future對象。通過Future.get可以判斷任務是否執行成功、或者執行完成。(主要區別)
【3】submit()方便主線程對Exception做處理。通過Future.get捕獲拋出異常。
注意:Future.get方法是阻塞的。

void execute(Runnable command)  :
在將來某個時間執行給定任務。可以在新線程中或者在現有池線程中執行該任務。如果無法將任務提交執行,或者因爲此執行程序已關閉,或者因爲已達到其容量,則該任務由當前 RejectedExecutionHandler處理。
submit被重載三次
Future<?> submit(Runnable task)  提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future。該 Future 的 get 方法在成功完成時將會返回null。如果拋出異常,get方法將捕獲異常。
Future submit(Runnable task,T result)  Runnable 任務用於執行,並返回一個表示該任務的 Future。該 Future 的 get 方法在成功完成時將會返回給定的結果(也就是預先傳遞的result參數)。如果拋出異常,get方法將捕獲異常。 submit(task,null)和submit(task)是一樣的。
Future submit(Callable task)  提交一個返回值的任務用於執行,返回一個表示任務的未決結果的 Future。該 Future 的 get 方法在成功完成時將會返回該任務的結果。 如果想立即阻塞任務的等待,則可以使用 result = exec.submit(aCallable).get(); 

public static void main(String[] args) throws InterruptedException, ExecutionException {
     BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
     ThreadPoolExecutor e= new ThreadPoolExecutor(20, 50, 1L, TimeUnit.MINUTES, workQueue);
//   Future<?> f = e.submit(new MyRunnable(),new Re());
     Future<String> f = e.submit(new MyCallable());
     System.out.println("future get : "+ f.get());
}

static class MyRunnable implements Runnable{
     public void run() {
           String name = "mm";
           System.out.println(name);
           throw new RuntimeException("haha");
     }
}
static class MyCallable implements Callable<String>{
     public String call() throws Exception {
           String name = "dd";
           System.out.println(name);
           Thread.sleep(5000);
           return name;
     }
}
static class Re{
     String re = "ok";
}
【1】execute方法性能較高,所以不需要返回值的時候儘量可以選用。
【2】如果需要返回值,可以實現Callable接口。如果只是需要得到空的結果使用submit就好。
【3】Callable和Future使用可以實現異步執行方法,異步獲取執行結果。

關閉線程池
通過調用shutdown或者shutdownNow方法來關閉線程池。原理是遍歷線程池中的工作線程,然後逐個調用interrupt方法來中斷線程,所以無法響應中斷的線程可能永遠不能停止。
shutdownNow:將線程池狀態設置爲STOP,停止所有正在執行或空閒的線程,並返回等待執行任務的列表。
shutdown:只是將線程狀態設置爲SHUTDOWN,然後終端空閒的線程。

調用了以上的任意一個,isShutdown都會返回true。當所有任務都關閉以後纔算線程池關閉成功,isTerminated方法才返回true。通常調用shutdown方法,如果任務不執行完也行,也可以使用shutdownNow。

合理配置線程池
1.判斷任務性質:CPU密集型、IO密集型、混合型
2.可以選擇使用優先級隊列處理優先級不同的任務。(注意:可以導致低優先級的任務永遠不執行)
3.建議使用有界隊列。可以增加系統的穩定性和預警能力,維持在一定的任務數量,可以設置爲幾千。


線程池的監控
在系統中使用了大量線程池,有必要對線程池進行監控,可以根據線程池使用情況快速定位問題。
taskCount:線程需要執行的任務數量
completedTaskCount:線程池已完成的任務書,小於等於taskCount
largestPoolSize:線程池曾經創建的最大線程數。可以用來判斷線程池是否滿過。
getPoolSize:線程池的線程數量。線程池不銷燬的話,線程池中的線程不會自動銷燬,所以這個數值只增不減。

還可以對線程池進行擴展。通過集成線程池,重寫線程池的beforeExecute、afterExecute和terminated方法。執行一些自定義的代碼來監控。
例如監控任務的平均時間,最大(最小)執行時間等。在線程池中默認是空方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章