什麼是線程池?
線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然後在創建線程後自動啓動這些任務。
線程池的好處:
- 降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的消耗
- 提高響應速度。當任務到達時,任務可以不需要等到線程創建就能執行
- 提高現場的可管理性。線程是稀缺資源。如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一分配、調優和監控。但是,要做到合理利用線程池,必須對其實現原理瞭如指掌
線程池的執行流程:
1.線程池判斷核心線程池裏的線程是否都在執行任務。如果不是,則創建一個新的工作線程來執行任務。如果核心線程池裏的線程都在執行任務,則進入下個流程。
2.線程池判斷工作隊列是否已滿。如果工作隊列沒有滿,則將新提交的任務存儲在這個工作隊列裏,如果工作隊列滿了,則進入下個流程。
3.線程池判斷線程池的線程是否都處於工作狀態。如果沒有,則創建一個新的工作線程來執行任務。如果已經滿了,則交給飽和策略來處理這個任務。
如圖:
線程池的工作原理代碼實現:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.當前池中線程比核心數少,新建一個線程執行任務
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.核心池已滿,但任務隊列未滿,添加到隊列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //如果這時被關閉了,拒絕任務
reject(command);
else if (workerCountOf(recheck) == 0) //如果之前的線程已被銷燬完,新建一個線程
addWorker(null, false);
}
//3.核心池已滿,隊列已滿,試着創建一個新線程
else if (!addWorker(command, false))
reject(command); //如果創建新線程失敗了,說明線程池被關閉或者線程池完全滿了,拒絕任務
}
而飽和策略有以下四種:
- AbortPo**licy:直接拋出異常
- CallerRunsP**olicy:只用調用者所在線程來運行任務
- DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務
- DiscardPolicy:不處理,丟棄掉
當然也可以根據應用場景需要來實現RejectedExecutionHandler接口自定義策略。如記錄日誌或者持久化存儲不能出爐的任務。
- keepAliveTime(線程活動保持時間):線程池的工作線程空閒後,保持存活的時間。所以,如果任務很多,並且每個任務執行的時間比較短,可以調大時間,提高線程的利用率。
- TimeUnit(線程活動保持時間的 單位):可選的單位有天(DAYS)、小時(HOURS)、分鐘(MINUTES)、毫秒(MILLISECONDS)、微妙(MICROSECONDS,千分之一毫秒)和納秒(NANOSECONDS,千分之一微妙)。
線程池分爲四種:
- newCachedThreadPool:創建一個可緩存線程池
- newFixedThreadPool:創建一個定長線程池
- newScheduledThreadPool:創建一個核心線程數固定,而非核心線程數不固定的線程池
- newSingleThreadExecutor:創建一個單線程化的線程池
詳細介紹:
- newCachedThreadPool,是一種線程數量不定的線程池,並且其最大線程數爲Integer.MAX_VALUE,這個數是很大的,一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。但是線程池中的空閒線程都有超時限制,這個超時時長是60秒,超過60秒閒置線程就會被回收。調用execute將重用以前構造的線程(如果線程可用)。這類線程池比較適合執行大量的耗時較少的任務,當整個線程池都處於閒置狀態時,線程池中的線程都會超時被停止。
- newFixedThreadPool 創建一個指定工作線程數量的線程池,每當提交一個任務就創建一個工作線程,當線程處於空閒狀態時,它們並不會被回收,除非線程池被關閉了,如果工作線程數量達到線程池初始的最大數,則將提交的任務存入到池隊列(沒有大小限制)中。由於newFixedThreadPool只有核心線程並且這些核心線程不會被回收,這樣它更加快速底相應外界的請求。
- newScheduledThreadPool創建一個線程池,它的核心線程數量是固定的,而非核心線程數是沒有限制的,並且當非核心線程閒置時會被立即回收,它可安排給定延遲後運行命令或者定期地執行。這類線程池主要用於執行定時任務和具有固定週期的重複任務。
- newSingleThreadExecutor這類線程池內部只有一個核心線程,以無界隊列方式來執行該線程,這使得這些任務之間不需要處理線程同步的問題,它確保所有的任務都在同一個線程中按順序中執行,並且可以在任意給定的時間不會有多個線程是活動的。
代碼演示:
public class ThreadPoolExecutorDemo {
//newCachedThreadPool
public static void CachedThreadPoolDemo() {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace(); }
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
}
}
//newFixedThreadPool
public static void FixedThreadPoolDemo() {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
//newScheduledThreadPool
public static void SchedulThreadPoolDemo() {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("delay 3 seconds");
}
}, 3, TimeUnit.SECONDS);
}
//newCachedThreadPool
public static void SingleThreadExecutorDemo() {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
public static void main(String[] args) {
CachedThreadPoolDemo();
FixedThreadPoolDemo();
SchedulThreadPoolDemo();
SingleThreadExecutorDemo();
}
}
在使用線程池時,Java給我們提供了創建線程池的一個類Executor,而我們創建時,一般使用它的子類ThreadPoolExecutor
繼承關係:
public class ThreadPoolExecutor extends AbstractExecutorService {}
ThreadPoolExecutor的幾種構造參數:
- corePoolSize:核心線程的數量。默認是沒有超時的,也就是說就算線程閒置,也不會被處理。但是如果設置了allowCoreTimeOut爲true,那麼當核心線程閒置時,會被回收。
- maximumPoolSize:最大線程池尺寸,被CAPACITY限制(2^29-1)。
- keepAliveTime:閒置線程被回收的時間限制
- unit:keepAliveTime的單位
- workQueue:用於存放任務的隊列 四種 有界隊列 無界隊列 同步隊列 優先級隊列
- threadFactory:創建線程的工廠類
- handler:當任務執行失敗時,使用handler通知調用者