參考文獻:
1 簡介
?除了①線程池么抗,使用線程還有三種方式辆影,分別是 ②繼承Thread類 ③實現(xiàn)Runnable接口④實現(xiàn)Callable接口扁达,這三種方式最后都需要新建和銷毀線程。在實際的高并發(fā)場景下反肋,往往線程數(shù)多伤塌,每個線程執(zhí)行任務(wù)耗時短茫负。上面三種新建線程的方式米同,新建和銷毀線程的時間消耗經(jīng)常會比實際執(zhí)行任務(wù)的耗時還要長,導致提交的任務(wù)不能立即響應(yīng)執(zhí)行映砖,并且新建和銷毀線程往往有一定的資源消耗翰撑;其次創(chuàng)建新線程的方式線程缺乏統(tǒng)一管理,容易出現(xiàn)線程阻塞的情況啊央。所以這里我們引入了一種復用線程執(zhí)行任務(wù)的方式-線程池眶诈,減少新建線程的次數(shù)。
?首先說說“任務(wù)”的概念瓜饥,實現(xiàn)Runnable或Callable接口創(chuàng)建的對象只能當做一個可以在線程中運行的任務(wù)逝撬,不是真正意義上的線程,所以最后還需要通過 Thread 來調(diào)用乓土∠艹保可以說任務(wù)是通過線程驅(qū)動從而執(zhí)行的。
2 原理
2.1 內(nèi)部邏輯結(jié)構(gòu)
2.2 核心參數(shù)
?ThreadPoolExecutor
類是線程池中最重要的類趣苏,可以通過在該類實例化時配置不同的參數(shù)傳入構(gòu)造器狡相,來實現(xiàn)自定義線程池;
// 創(chuàng)建線程池對象食磕,通過 構(gòu)造方法 配置核心參數(shù)
Executor executor = new ThreadPoolExecutor( CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue,sThreadFactory );
// 構(gòu)造函數(shù)
public ThreadPoolExecutor (int corePoolSize,int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable workQueue>,
ThreadFactory threadFactory )
2.3 任務(wù)執(zhí)行邏輯
?補充說明:
- 處理任務(wù)優(yōu)先級:核心線程 > 任務(wù)隊列 > 非核心線程 > 拒絕策略handler尽棕;
- 非核心線程空閑時間超過存活期keepAliveTime,就會被回收彬伦;一般情況滔悉,核心線程即使閑置也不會被回收伊诵;(除非當allowCoreThreadTimeOut(true)時,keepAliveTime也適用于核心線程)回官;
- 一般情況曹宴,線程池剛創(chuàng)建時,其中是沒有線程的歉提,只有新任務(wù)到來笛坦,才會創(chuàng)建線程執(zhí)行該任務(wù);
? 對于線程池苔巨、核心線程版扩、非核心線程自己的通俗理解:
? ?線程池看成一個公司,核心線程可以看成是看成是公司的正式員工恋拷,非核心線程可以看成是實習生;無論是正式員工還是實習生在同一時刻只能執(zhí)行一個任務(wù)厅缺。當有新任務(wù)時蔬顾,老板首先找正式員工干活;如果正式員工都在干活時繼續(xù)來新任務(wù)湘捎,那么多余任務(wù)就放到一個任務(wù)隊列里诀豁,等有正式員工閑下來后接著干新任務(wù);如果任務(wù)較多窥妇,超出正式員工能處理的數(shù)量舷胜,這時老板就考慮招幾個實習生來干活,實習生處理任務(wù)隊列滿了之后新來的任務(wù)活翩。如果任務(wù)太多了烹骨,那么老板就不再接單了,拒絕處理新來的任務(wù)材泄。
? ?一般情況沮焕,正式員工有任務(wù)到來就干活,沒任務(wù)就空閑拉宗,不會被裁峦树;實習生有任務(wù)到來就干活,沒任務(wù)就空閑旦事,空閑的時間太長了魁巩,老板不養(yǎng)閑人,就把這個實習生裁了姐浮。
3 基本使用
// 1.創(chuàng)建線程池,通過配置核心參數(shù)谷遂,從而實現(xiàn)自定義線程池
Executor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,
TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
// 注:在Java中,已內(nèi)置4種常用線程池卖鲤,下面會詳細說明
// 2.向線程池提交任務(wù):execute()埋凯,傳入Runnable對象
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 線程執(zhí)行任務(wù)
}
});
// 3. 關(guān)閉線程池shutdown()
threadPool.shutdown();
?shutdown()
和shutdownNow()
區(qū)別:
-
shutdown()
:等待正在執(zhí)行任務(wù)的線程執(zhí)行完再關(guān)閉線程池点楼; -
shutdownNow()
:不等待立即關(guān)閉;
4 四類常用線程池
?根據(jù)參數(shù)的不同配置白对,Java內(nèi)置了4種常用線程池掠廓,他們的參數(shù)已經(jīng)配置好了:
- 定長線程池(FixedThreadPool)
- 定時線程池(ScheduledThreadPool )
- 緩存線程池(CachedThreadPool)
- 單例線程池(SingleThreadExecutor)
4.1 定長線程池FixedThreadPool
特點:只有核心線程 & 不會被回收、線程數(shù)固定甩恼、任務(wù)隊列無大小限制蟀瞧;
應(yīng)用場景:控制線程池最大并發(fā)數(shù);
//創(chuàng)建 定長線程池 對象 & 設(shè)置線程池線程數(shù)固定為3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
//向線程池提交任務(wù):execute()
fixedThreadPool.execute(() -> System.out.println("定長線程池->執(zhí)行任務(wù)啦"));
//關(guān)閉線程池
fixedThreadPool.shutdown();
4.2 定時線程池ScheduledThreadPool
特點:核心線程數(shù)固定、非核心線程數(shù)無限制(閑時立刻回收)条摸;
應(yīng)用場景:執(zhí)行定時 / 周期性 任務(wù)
//創(chuàng)建 定時線程池 對象 & 設(shè)置線程池核心線程數(shù)固定為5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//創(chuàng)建好Runnable類線程對象 & 需執(zhí)行的任務(wù)
Runnable task1 = () -> System.out.println("定時線程池->執(zhí)行任務(wù)啦");
// 向線程池提交任務(wù):schedule()
scheduledThreadPool.schedule(task1, 1, TimeUnit.SECONDS); // 延遲1s后執(zhí)行任務(wù)
scheduledThreadPool.scheduleAtFixedRate(task1,10,1000,TimeUnit.MILLISECONDS);// 延遲10ms后悦污、每隔1000ms執(zhí)行任務(wù)
//關(guān)閉線程池
scheduledThreadPool.shutdown();
4.3 緩存線程池CachedThreadPool
- 特點:只有非核心線程、線程數(shù)無限制钉蒲;靈活回收空閑線程(具備超時機制切端,全部回收時幾乎不占系統(tǒng)資源);無線程可用時新建線程顷啼;
- 應(yīng)用場景:執(zhí)行數(shù)量多踏枣、耗時少任務(wù)
// 創(chuàng)建 可緩存線程池 對象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//向線程池提交任務(wù)1:execute()
cachedThreadPool.execute(() -> System.out.println("可緩存線程池-執(zhí)行任務(wù)1"));
//向線程池提交任務(wù)2:execute();復用線程
cachedThreadPool.execute(() -> System.out.println("可緩存線程池-執(zhí)行任務(wù)2"));
//關(guān)閉線程池
cachedThreadPool.shutdown();
4.4 單例線程池SingleThreadExecutor
特點:只有一個核心線程钙蒙,保證所有任務(wù)按順序在一個線程中執(zhí)行茵瀑,不存在線程安全問題;
應(yīng)用場景:適合單線程任務(wù)躬厌;不適合必須使用多線程的耗時任務(wù)马昨,如數(shù)據(jù)庫操作、文件操作等扛施;
//創(chuàng)建 單例線程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//向線程池提交任務(wù):execute()
singleThreadExecutor.execute(() -> System.out.println("單例線程池-執(zhí)行任務(wù)啦"));
//關(guān)閉線程池
singleThreadExecutor.shutdown();