參考文獻:
1 簡介
除了①線程池,使用線程還有三種方式,分別是 ②繼承Thread類 ③實現Runnable接口④實現Callable接口,這三種方式最後都需要新建和銷燬線程。在實際的高併發場景下,往往線程數多,每個線程執行任務耗時短。上面三種新建線程的方式,新建和銷燬線程的時間消耗經常會比實際執行任務的耗時還要長,導致提交的任務不能立即響應執行,並且新建和銷燬線程往往有一定的資源消耗;其次創建新線程的方式線程缺乏統一管理,容易出現線程阻塞的情況。所以這裏我們引入了一種複用線程執行任務的方式-線程池,減少新建線程的次數。
首先說說“任務”的概念,實現Runnable或Callable接口創建的對象只能當做一個可以在線程中運行的任務,不是真正意義上的線程,所以最後還需要通過 Thread 來調用。可以說任務是通過線程驅動從而執行的。
2 原理
2.1 內部邏輯結構
2.2 核心參數
參數 | 意義 | 說明 |
---|---|---|
corePoolSize | 核心線程數 | 默認情況,核心線程會一直存活,包括空閒狀態 |
maximumPoolSize | (能容納)最大線程數 | 當已提交的任務數大於等於該值,後面新提交的任務會使用拒絕策略handler |
keepAliveTime | 非核心線程閒時存活期 | 超過該時長,處於閒置狀態的非核心線程會被回收(當allowCoreThreadTimeOut(true)時,keepAliveTime也適用於核心線程) |
unit | keepAliveTime單位 | TimeUnit.DAYS; TimeUnit.HOURS;TimeUnit.MINUTES; TimeUnit.SECONDS;TimeUnit.MILLISECONDS;TimeUnit.MICROSECONDS; |
workQueue | 任務隊列 | 當corePoolSize個核心線程都運行時,新來的任務就放到這個隊列裏等待執行(就緒狀態) |
threadFactor | 線程工廠接口 | 爲線程池創建新線程 |
ThreadPoolExecutor
類是線程池中最重要的類,可以通過在該類實例化時配置不同的參數傳入構造器,來實現自定義線程池;
// 創建線程池對象,通過 構造方法 配置核心參數
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 )
2.3 任務執行邏輯
- 處理任務優先級:核心線程 > 任務隊列 > 非核心線程 > 拒絕策略handler;
- 非核心線程空閒時間超過存活期keepAliveTime,就會被回收;一般情況,核心線程即使閒置也不會被回收;(除非當allowCoreThreadTimeOut(true)時,keepAliveTime也適用於核心線程)
- 一般情況,線程池剛創建時,其中是沒有線程的,只有新任務到來,纔會創建線程執行該任務;
對於線程池、核心線程、非核心線程自己的通俗理解:
線程池看成一個公司,核心線程可以看成是看成是公司的正式員工,非核心線程可以看成是實習生;無論是正式員工還是實習生在同一時刻只能執行一個任務。當有新任務時,老闆首先找正式員工幹活;如果正式員工都在幹活時繼續來新任務,那麼多餘任務就放到一個任務隊列裏,等有正式員工閒下來後接着幹新任務;如果任務較多,超出正式員工能處理的數量,這時老闆就考慮招幾個實習生來幹活,實習生處理任務隊列滿了之後新來的任務。如果任務太多了,那麼老闆就不再接單了,拒絕處理新來的任務。
一般情況,正式員工有任務到來就幹活,沒任務就空閒,不會被裁;實習生有任務到來就幹活,沒任務就空閒,空閒的時間太長了,老闆不養閒人,就把這個實習生裁了。
3 基本使用
// 1.創建線程池,通過配置核心參數,從而實現自定義線程池
Executor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,
TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
// 注:在Java中,已內置4種常用線程池,下面會詳細說明
// 2.向線程池提交任務:execute(),傳入Runnable對象
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 線程執行任務
}
});
// 3. 關閉線程池shutdown()
threadPool.shutdown();
shutdown()
和shutdownNow()
區別:
shutdown()
:等待正在執行任務的線程執行完再關閉線程池;shutdownNow()
:不等待立即關閉;
4 四類常用線程池
根據參數的不同配置,Java內置了4種常用線程池,他們的參數已經配置好了:
- 定長線程池(FixedThreadPool)
- 定時線程池(ScheduledThreadPool )
- 緩存線程池(CachedThreadPool)
- 單例線程池(SingleThreadExecutor)
4.1 定長線程池FixedThreadPool
-
特點:只有核心線程 & 不會被回收、線程數固定、任務隊列無大小限制;
-
應用場景:控制線程池最大併發數;
//創建 定長線程池 對象 & 設置線程池線程數固定爲3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
//向線程池提交任務:execute()
fixedThreadPool.execute(() -> System.out.println("定長線程池->執行任務啦"));
//關閉線程池
fixedThreadPool.shutdown();
4.2 定時線程池ScheduledThreadPool
-
特點:核心線程數固定、非核心線程數無限制(閒時立刻回收);
-
應用場景:執行定時 / 週期性 任務
//創建 定時線程池 對象 & 設置線程池核心線程數固定爲5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//創建好Runnable類線程對象 & 需執行的任務
Runnable task1 = () -> System.out.println("定時線程池->執行任務啦");
// 向線程池提交任務:schedule()
scheduledThreadPool.schedule(task1, 1, TimeUnit.SECONDS); // 延遲1s後執行任務
scheduledThreadPool.scheduleAtFixedRate(task1,10,1000,TimeUnit.MILLISECONDS);// 延遲10ms後、每隔1000ms執行任務
//關閉線程池
scheduledThreadPool.shutdown();
4.3 緩存線程池CachedThreadPool
- 特點:只有非核心線程、線程數無限制;靈活回收空閒線程(具備超時機制,全部回收時幾乎不佔系統資源);無線程可用時新建線程;
- 應用場景:執行數量多、耗時少任務
// 創建 可緩存線程池 對象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//向線程池提交任務1:execute()
cachedThreadPool.execute(() -> System.out.println("可緩存線程池-執行任務1"));
//向線程池提交任務2:execute();複用線程
cachedThreadPool.execute(() -> System.out.println("可緩存線程池-執行任務2"));
//關閉線程池
cachedThreadPool.shutdown();
4.4 單例線程池SingleThreadExecutor
-
特點:只有一個核心線程,保證所有任務按順序在一個線程中執行,不存在線程安全問題;
-
應用場景:適合單線程任務;不適合必須使用多線程的耗時任務,如數據庫操作、文件操作等;
//創建 單例線程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//向線程池提交任務:execute()
singleThreadExecutor.execute(() -> System.out.println("單例線程池-執行任務啦"));
//關閉線程池
singleThreadExecutor.shutdown();
4.5 對比
線程池類型 | 池內 線程類型 | 池內 線程數 | 處理特點 | 應用場景 | BlockingQueue |
---|---|---|---|---|---|
定長線程池FixedThreadPool |
核心線程 | 核心線程數固定 | ①核心線程不被回收②任務隊列大小無限制 | 控制線程池最大併發數 | LinkedBlockingQueue |
定時線程池ScheduledThreadPool |
核心線程,非核心線程, | 核心線程數固定,非核心線程數無限制 | 非核心線程閒時立刻回收(keepAliveTime爲0) | 執行定時 / 週期性任務 | DelayedWorkQueue |
緩存線程池CachedThreadPool |
非核心線程 | 非核心線程數無限制 | ①有閒置線程,複用線程;否則新建線程;②一般,提交任務立刻執行③非核心線程閒時靈活回收 | 執行數量多、耗時少任務 | SynchronousQueue |
單例線程池SingleThreadExecutor |
核心線程 | 1個核心線程 | ①所有任務按FIFO順序在一個線程中執行②不存在線程安全問題 | 適合單線程任務;不適合必須使用多線程的耗時任務 | LinkedBlockingQueue |