一、線程是稀缺資源,使用線程池能夠減小建立和銷燬線程的次數,每一個工做線程均可以重複使用。 設置線程的個數請看極客時間上這邊文章的介紹:time.geekbang.org/column/arti…, cpu密集型:cpu核數+1;IO密集型:單核:1 +(I/O 耗時 / CPU 耗時),多核:CPU 核數 * [ 1 +(I/O 耗時 / CPU 耗時)]數組
二、能夠根據系統的承受能力,調整線程池中工做線程的數量,防止由於消耗過多內存致使服務器崩潰。緩存
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
複製代碼
corePoolSize:線程池核心線程數量
maximumPoolSize:線程池最大線程數量
keepAliverTime:當活躍線程數大於核心線程數時,空閒的多餘線程最大存活時間
unit:存活時間的單位
workQueue:存聽任務的隊列
handler:超出線程範圍和隊列容量的任務的處理程序
注:關於workQueue參數的取值,JDK提供了4種阻塞隊列類型供選擇:
ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按FIFO排序任務;
inkedBlockingQuene:基於鏈表結構的阻塞隊列,按FIFO排序任務,吞吐量一般要高於ArrayBlockingQuene
SynchronousQuene:一個不存儲元素的阻塞隊列,每一個插入操做必須等到另外一個線程調用移除操做,不然插入操做一直處於阻塞狀態,吞吐量一般要高於ArrayBlockingQuene;
PriorityBlockingQuene:具備優先級的無界阻塞隊列;
threadFactory:線程工廠,主要用來建立線程;
handler:表示當拒絕處理任務時的策略,有如下四種取值
注: 當線程池的飽和策略,當阻塞隊列滿了,且沒有空閒的工做線程,若是繼續提交任務,必須採起一種策略處理該任務,線程池提供了4種策略:
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,可是不拋出異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,而後從新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
固然也能夠根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日誌或持久化存儲不能處理的任務。
複製代碼
提交一個任務到線程池中,線程池的處理流程以下:
一、判斷線程池裏的核心線程是否都在執行任務,若是不是(核心線程空閒或者還有核心線程沒有被建立)則建立一個新的工做線程來執行任務。若是核心線程都在執行任務,則進入下個流程。
二、線程池判斷工做隊列是否已滿,若是工做隊列沒有滿,則將新提交的任務存儲在這個工做隊列裏。若是工做隊列滿了,則進入下個流程。
三、判斷線程池裏的線程是否都處於工做狀態,若是沒有,則建立一個新的工做線程來執行任務。若是已經滿了,則交給飽和策略來處理這個任務。
複製代碼
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//若是線程數大於等於基本線程數或者線程建立失敗,將任務加入隊列
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
//線程池處於運行狀態而且加入隊列成功
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
}
//線程池不處於運行狀態或者加入隊列失敗,則建立線程(建立的是非核心線程)
else if (!addIfUnderMaximumPoolSize(command))
//建立線程失敗,則採起阻塞處理的方式
reject(command); // is shutdown or saturated
}
}
複製代碼
說明:初始化一個指定線程數的線程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene做爲阻塞隊列
特色:即便當線程池沒有可執行任務時,也不會釋放線程。
複製代碼
說明:初始化一個能夠緩存線程的線程池,默認緩存60s,線程池的線程數可達到Integer.MAX_VALUE,即2147483647,內部使用SynchronousQueue做爲阻塞隊列;
特色:在沒有任務執行時,當線程的空閒時間超過keepAliveTime,會自動釋放線程資源;當提交新任務時,若是沒有空閒線程,則建立新線程執行任務,會致使必定的系統開銷;
所以,使用時要注意控制併發的任務數,防止因建立大量的線程致使而下降性能。
複製代碼
說明:初始化只有一個線程的線程池,內部使用LinkedBlockingQueue做爲阻塞隊列。
特色:若是該線程異常結束,會從新建立一個新的線程繼續執行任務,惟一的線程能夠保證所提交任務的順序執行
複製代碼
特定:初始化的線程池能夠在指定的時間內週期性的執行所提交的任務,在實際的業務場景中可使用該線程池按期的同步數據。
總結:除了newScheduledThreadPool的內部實現特殊一點以外,其它線程池內部都是基於ThreadPoolExecutor類(Executor的子類)實現的。
複製代碼
有兩種方式:bash
Executor.execute(Runnable command);
ExecutorService.submit(Callable<T> task);
複製代碼
1.首次經過workCountof()獲知當前線程池中的線程數,
若是小於corePoolSize, 就經過addWorker()建立線程並執行該任務;
 不然,將該任務放入阻塞隊列;
2. 若是能成功將任務放入阻塞隊列中,
若是當前線程池是非RUNNING狀態,則將該任務從阻塞隊列中移除,而後執行reject()處理該任務;
若是當前線程池處於RUNNING狀態,則須要再次檢查線程池(由於可能在上次檢查後,有線程資源被釋放),是否有空閒的線程;若是有則執行該任務;
三、若是不能將任務放入阻塞隊列中,說明阻塞隊列已滿;那麼將經過addWoker()嘗試建立一個新的線程去執行這個任務;若是addWoker()執行失敗,說明線程池中線程數達到maxPoolSize,則執行reject()處理任務;
複製代碼
會將提交的Callable任務會被封裝成了一個FutureTask對象
FutureTask類實現了Runnable接口,這樣就能夠經過Executor.execute()提交FutureTask到線程池中等待被執行,最終執行的是FutureTask的run方法;
比較:
兩個方法均可以向線程池提交任務,execute()方法的返回類型是void,它定義在Executor接口中, 而submit()方法能夠返回持有計算結果的Future對象,它定義在ExecutorService接口中,它擴展了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。
複製代碼
當線程被建立並啓動後, 並非一啓動就進入執行狀態,也不是一直處於執行狀態.在線程的生命週期中,要通過新建、就緒、運行、阻塞、死亡五種狀態.服務器
當程序使用new關鍵字建立了一個線程以後,該線程就處於新建狀態,此時僅由JVMJ爲其分配內存,並初始化其成員變量的值.併發
當線程對象調用了 start()方法以後,該線程處於就緒狀態。Java 虛擬機會爲其建立方法調用棧和 程序計數器,等待調度運行。性能
若是處於就緒狀態的線程得到了cpu,開始執行run()方法執行體,則該線程處於運行狀態.ui
阻塞狀態是指線程由於某種緣由放棄了cpu使用權,也即讓出了cpu時間片,暫停中止運行.直到線程進入可運行狀態,纔有機會再次得到cpu時間片轉到運行狀態. 線程狀態之間到轉換spa