前言:當我們線程創(chuàng)建過多時州藕,容易引發(fā)內(nèi)存溢出森篷,這時我們?nèi)绾谓鉀Q這個問題呢?
一营罢、什么是線程池
顧名思義赏陵,線程池就是一個用來裝線程的池子(容器)。首先需要創(chuàng)建若干個可執(zhí)行的線程放入一個池中饲漾,有任務(wù)需要處理時蝙搔,會提交到線程池中的任務(wù)隊列,處理完之后線程并不會被銷毀能颁,而是仍然在線程池中等待下一個任務(wù)杂瘸。
二、線程池原理詳解
線程池原理圖
首先需要創(chuàng)建一個線程池伙菊,然后需要創(chuàng)建若干個可執(zhí)行的線程放入一個池中(Thread1-5)败玉,但是只開啟一部分默認的核心線程(例如Thread1-3),接下來有任務(wù)進入并且假設(shè)都長時間持續(xù)占有線程時镜硕,當只有幾個(這里如果是小于等于三個)任務(wù)時运翼,會按順序分配到三個核心線程中,如果大于三個則會進入到任務(wù)隊列(workQueue,Task4-7)中等待兴枯,如果任務(wù)超過了workQueue的最大容納量血淌,則創(chuàng)建新的線程(Thread4-5),并將workQueue中的任務(wù)按順序分別到線程中财剖,Task8-9進入workQueue悠夯,如果還有Task加入,那么調(diào)用拒絕策略方法躺坟,防止Task進入沦补;如果線程Thread4-5執(zhí)行完任務(wù)后在一定時間內(nèi)沒有任務(wù)進入,則會被回收咪橙。
三夕膀、線程池的優(yōu)點
因此,從上面的分析來看美侦,對比不使用線程池的情況(即創(chuàng)建線程對象产舞、執(zhí)行任務(wù)、釋放線程對象)菠剩,線程池具有以下優(yōu)點:
- 降低資源消耗易猫。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。(前面的Task先執(zhí)行完后后面的Task可以復(fù)用前面的線程)
- 提高響應(yīng)速度具壮。當任務(wù)到達時擦囊,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行违霞。(提前創(chuàng)建好核心線程Thread1-3)
- 提高線程的可管理性。線程是稀缺資源瞬场,如果無限制的創(chuàng)建买鸽,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性贯被,使用線程池可以進行統(tǒng)一的分配眼五,調(diào)優(yōu)和監(jiān)控。
四彤灶、線程池的參數(shù)含義
首先看幼,我們來看源碼:
Executor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory
);
public ThreadPoolExecutor (int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable workQueue>,
ThreadFactory threadFactory )
- corePoolSize:核心線程數(shù)。默認情況下幌陕,核心線程會一直存活诵姜,但是當將 allowCoreThreadTimeout 設(shè)置為 true 時,核心線程也會超時回收搏熄。
- maximumPoolSize:線程池所能容納的最大線程數(shù)棚唆。當活躍線程數(shù)達到該數(shù)值后,后續(xù)的新任務(wù)將會阻塞心例。
- keepAliveTime:線程閑置超時時長荔烧。如果超過該時長楚午,非核心線程就會被回收峡碉。
- unit:指定 keepAliveTime 參數(shù)的時間單位葛虐。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)译株、TimeUnit.MINUTES(分)瓜喇。
- workQueue:任務(wù)隊列。通過線程池的 execute() 方法提交的 Runnable 對象將存儲在該參數(shù)中歉糜。其采用阻塞隊列實現(xiàn)乘寒。
- threadFactory(可選):線程工廠。用于指定為線程池創(chuàng)建新線程的方式现恼。
- handler(可選):拒絕策略。當達到最大線程數(shù)時需要執(zhí)行的飽和策略黍檩。
五叉袍、線程池的使用流程
// 創(chuàng)建線程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory);
// 向線程池提交任務(wù)
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 線程執(zhí)行的任務(wù)
}
});
// 關(guān)閉線程池
threadPool.shutdown(); // 設(shè)置線程池的狀態(tài)為SHUTDOWN,然后中斷所有沒有正在執(zhí)行任務(wù)的線程
threadPool.shutdownNow(); // 設(shè)置線程池的狀態(tài)為 STOP刽酱,然后嘗試停止所有的正在執(zhí)行或暫停任務(wù)的線程喳逛,并返回等待執(zhí)行任務(wù)的列表
六、常見線程池
- 定長線程池(FixedThreadPool):只有核心線程棵里,線程數(shù)量固定润文,執(zhí)行完立即回收姐呐,任務(wù)隊列為鏈表結(jié)構(gòu)的有界隊列。
- 定時線程池(ScheduledThreadPool):核心線程數(shù)量固定典蝌,非核心線程數(shù)量無限曙砂,執(zhí)行完閑置 10ms 后回收,任務(wù)隊列為延時阻塞隊列骏掀。
- 可緩存線程池(CachedThreadPool):無核心線程鸠澈,非核心線程數(shù)量無限,執(zhí)行完閑置 60s 后回收截驮,任務(wù)隊列為不存儲元素的阻塞隊列笑陈。
- 單線程化線程池(SingleThreadExecutor):只有 1 個核心線程,無非核心線程葵袭,執(zhí)行完立即回收涵妥,任務(wù)隊列為鏈表結(jié)構(gòu)的有界隊列。
七坡锡、拒絕策略
- AbortPolicy(默認):丟棄任務(wù)并拋出 RejectedExecutionException 異常蓬网。
- CallerRunsPolicy:由調(diào)用線程處理該任務(wù)。
- DiscardPolicy:丟棄任務(wù)娜氏,但是不拋出異常拳缠。可以配合這種模式進行自定義的處理方式贸弥。
- DiscardOldestPolicy:丟棄隊列最早的未處理任務(wù)窟坐,然后重新嘗試執(zhí)行任務(wù)。