一、線程池概述
1、線程池的優勢
-
線程池是一種線程使用模式,線程過多會帶來調度開銷,進而影響緩存局部性和整體性能,而線程池維護著多個線程,等待著監督管理者分配可并發執行的任務,這避免了在處理短時間任務時創建與銷毀線程的代價,線程池不僅能夠保證內核的充分利用,還能防止過分調度
-
線程池的工作主要是控制運行的線程數量,在處理過程中將任務放入隊列,然后在線程創建后啟動這些任務,如果線程數量超過了最大數量,超出數量的線程排隊等候,等其他線程執行完畢,再從隊列中取出任務來執行
2、線程池的特點
-
降低資源消耗: 通過重復利用已創建的線程降低線程創建和銷毀造成的銷耗
-
提高響應速度: 當任務到達時,任務可以不需要等待線程創建就能立即執行
-
提高線程的可管理性: 線程是稀缺資源,如果無限制的創建,不僅會銷耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控
二、線程池的架構
- Java 中的線程池是通過 Executor 框架實現的,該框架中用到了 Executor 接口,Executors 類,ExecutorService 類,ThreadPoolExecutor 類

三、線程池的種類與創建
1、一池 N 線程
(1)基本介紹
-
線程池中的線程處于一定的量,可以很好的控制線程的并發量
-
線程可以重復被使用,在顯示關閉之前,都將一直存在
-
超出一定量的線程被提交時候需在隊列中等待
(2)創建方式
ExecutorService threadPool = Executors.newFixedThreadPool(5);
(3)適用場景
- 適用于可以預測線程數量的業務,或者服務器負載較重,對線程數有嚴格限制的場景
(4)基本使用
- 使用 5 個窗口處理 10 個顧客請求
ExecutorService threadPool = Executors.newFixedThreadPool(5);try {for (int i = 1; i <= 10; i++) {// 執行線程threadPool.execute(() -> {System.out.println(Thread.currentThread().getName()+" 辦理業務");});}
} catch (Exception e) {e.printStackTrace();
} finally {// 關閉線程threadPool.shutdown();
}
- 結果
pool-1-thread-1 辦理業務
pool-1-thread-5 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-2 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-5 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-2 辦理業務
2、一池一線程
(1)基本介紹
- 線程池中最多執行一個線程,之后提交的線程活動將會排在隊列中依次執行
(2)創建方式
ExecutorService threadPool = Executors.newSingleThreadExecutor();
(3)適用場景
- 適用于需要保證順序執行各個任務,并且在任意時間點,不會同時有多個線程的場景
(4)基本使用
- 使用 1 個窗口處理 10 個顧客請求
ExecutorService threadPool = Executors.newSingleThreadExecutor();try {for (int i = 1; i <= 10; i++) {// 執行線程threadPool.execute(() -> {System.out.println(Thread.currentThread().getName()+" 辦理業務");});}
} catch (Exception e) {e.printStackTrace();
} finally {// 關閉線程threadPool.shutdown();
}
- 結果
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-1 辦理業務
3、一池可擴容線程
(1)基本介紹
-
線程池中數量沒有固定,可達到最大值為 Interger. MAX_VALUE
-
線程池中的線程可進行緩存重復利用和回收,默認回收時間為一分鐘
-
當線程池中沒有可用線程時,會重新創建一個線程
(2)創建方式
ExecutorService threadPool = Executors.newCachedThreadPool();
(3)適用場景
- 適用于創建一個可無限擴大的線程池,服務器負載壓力較輕,執行時間較短,任務多的場景
(4)基本使用
- 處理 20 個顧客請求
ExecutorService threadPool = Executors.newCachedThreadPool();try {for (int i = 1; i <= 20; i++) {// 執行線程threadPool.execute(() -> {System.out.println(Thread.currentThread().getName()+" 辦理業務");});}
} catch (Exception e) {e.printStackTrace();
} finally {// 關閉線程threadPool.shutdown();
}
- 結果
pool-1-thread-1 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-2 辦理業務
pool-1-thread-6 辦理業務
pool-1-thread-5 辦理業務
pool-1-thread-8 辦理業務
pool-1-thread-7 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-7 辦理業務
pool-1-thread-5 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-2 辦理業務
pool-1-thread-6 辦理業務
pool-1-thread-8 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-9 辦理業務
pool-1-thread-7 辦理業務
pool-1-thread-5 辦理業務
四、線程池的底層原理
1、ThreadPoolExecutor 類
- 通過查看上面三種方式創建線程池對象的源代碼,發現都有 new ThreadPoolExecutor 操作,具體查看該類的源代碼,其構造器涉及七個參數
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)throw new IllegalArgumentException();if (workQueue == null || threadFactory == null || handler == null)throw new NullPointerException();this.acc = System.getSecurityManager() == null ?null :AccessController.getContext();this.corePoolSize = corePoolSize;this.maximumPoolSize = maximumPoolSize;this.workQueue = workQueue;this.keepAliveTime = unit.toNanos(keepAliveTime);this.threadFactory = threadFactory;this.handler = handler;
}
2、ThreadPoolExecutor 構造器涉及的七個參數
-
int corePoolSize:常駐線程數量
-
int maximumPoolSize:最大線程數量
-
long keepAliveTime:空閑線程存活時間
-
TimeUnit unit:空閑線程存時間單位
-
BlockingQueue workQueue:阻塞隊列,存放提交但未執行任務
-
ThreadFactory threadFactory:線程工廠,用于創建線程(可省略)
-
RejectedExecutionHandler handler:阻塞隊列滿時的拒絕策略(可省略)
五、線程池的工作流程

-
在創建了線程池后,線程池中的線程數為零
-
當調用 execute 方法添加一個請求任務時,線程池會做如下判斷
-
如果正在運行的線程數量小于 corePool 的線程數量,那么馬上創建線程運行這個任務
-
如果正在運行的線程數量大于或等于 corePool 的線程數量,那么將這個任務存入阻塞隊列
-
如果這個時候隊列已滿且正在運行的線程數量小于 maximumPool 的線程數量,則創建非核心線程立刻運行這個任務
-
如果隊列已滿且正在運行的線程數量大于或等于 maximumPool 的線程數量,那么線程池會啟動拒絕策略
-
-
當一個線程完成任務時,它會從隊列中取下一個任務來執行
-
當一個線程空閑超過一定的時間時,會做如下判斷
-
如果當前運行的線程數大于 corePool 的線程數量,那么這個線程就被停掉
-
線程池的所有任務完成后,最終會收縮到 corePool 的大小
-
六、線程池的拒絕策略
1、 AbortPolicy
- 默認的拒絕策略,會直接拋出 RejectedExecutionException 異常阻止系統正常運行
2、DiscardPolicy
- 該策略會丟棄無法處理的任務,不給予任何處理也不拋出異常,如果允許任務丟失,這是最好的一種策略
3、DiscardOldestPolicy
- 丟棄隊列中等待最久的任務,然后把當前任務存入隊列中,并嘗試提交當前任務
4、CallerRunsPolicy
- 該策略既不會丟棄任務,也不會拋出異常,而是將任務退回給調用者
七、自定義線程池
1、Executors 返回的線程池對象的弊端
-
FixedThreadPool 和 SingleThreadExecutor 允許請求隊列的長度為 Interger.MAX_VALUE,可能會堆積大量異常, 從而導致 OOM
-
CachedThreadPool 允許的創建線程數量為 Interger.MAX_VALUE,可能會創建大量線程,從而導致 OOM
2、具體實現
package com.my.pool;import java.util.concurrent.*;public class MyThreadPoolTest {public static void main(String[] args) {ExecutorService threadPool = new ThreadPoolExecutor(2,5,2L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.DiscardOldestPolicy());// 處理 10 個顧客請求try {for (int i = 1; i <= 10; i++) {// 執行線程threadPool.execute(() -> {System.out.println(Thread.currentThread().getName()+" 辦理業務");});}} catch (Exception e) {e.printStackTrace();} finally {// 關閉線程threadPool.shutdown();}}
}
- 結果
pool-1-thread-1 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-2 辦理業務
pool-1-thread-3 辦理業務
pool-1-thread-4 辦理業務
pool-1-thread-1 辦理業務
pool-1-thread-5 辦理業務