目錄
一、線程池的概念
二、Java 標準庫中的線程池類
2.1 ThreadPoolExecutor 類
2.1.1?corePoolSize 和?maximumPoolSize
2.1.2?keepAliveTime 和?unit
2.1.3?workQueue
2.1.4?threadFactory
2.1.5?handler
2.1.6 創建一個參數自定義的線程池
2.2?Executors 類
2.3 實現自己的線程池
一、線程池的概念
1)什么是線程池? |
準備預期需要使用的對象,將這些對象放入一個可以隨時取用的容器中,這個容器就被稱為“池”。 使用過的對象也不立刻銷毀,而是放回“池”中,以備下次使用。 將線程作為上述對象,放在一個容器中,這就稱為“線程池”。 |
2)為什么使用線程池? |
在實際使用中,“線程池”并沒有頻繁的創建和銷毀線程,而是從“池”中存取線程,這樣可以減少每次啟動和銷毀線程的損耗,提高運行效率,減小系統開銷。 |
3)為什么使用線程池可以提高效率? |
通常創建線程,是通過向系統申請創建,需要通過系統內核完成,是具有內核態的代碼。 而從線程池中存取線程,是屬于用戶態的代碼,相比于內核態代碼更可控、穩定,操作更快。 |
二、Java 標準庫中的線程池類
2.1 ThreadPoolExecutor 類
這個類的構造方法參數眾多,包含以下參數: | |
int corePoolSize | 核心線程數 |
int maximumPoolSize | 最大線程數 |
long keepAliveTime | 空閑線程存活時間 |
TimeUnit unit | 時間單位 |
BlockingQueue<Runnable> workQueue | 任務隊列 |
ThreadFactory threadFactory | 線程工廠 |
RejectedExecutionHandler handler | 拒絕策略 |
2.1.1?corePoolSize 和?maximumPoolSize
int corePoolSize?核心線程數 | |
線程池中保持持有的最小線程數量。 | |
int maximumPoolSize?最大線程數 | |
線程池中可以持有的最大線程數量。 |
標準庫提供的線程池,持有的線程數量是可以變動的,可以根據需要執行的任務數量,自適應線程的數量。 |
2.1.2?keepAliveTime 和?unit
long keepAliveTime?空閑線程存活時間 | |
當線程池中的線程處于空閑狀態時,會等待 keepAliveTime 時間后自動關閉。 如果keepAliveTime為0,線程在執行完任務后則不會關閉。 | |
TimeUnit unit?時間單位 | |
keepAliveTime?空閑線程存活時間的時間單位。 |
2.1.3?workQueue
BlockingQueue<Runnable> workQueue?任務隊列 | |
用于存儲等待執行的任務。當線程池中的線程數量達到最大值時,新任務將被添加到隊列中等待執行。 執行的任務應該是 Runnable 接口的實現類, |
2.1.4?threadFactory
前置知識點:工廠模式
什么是工廠模式? |
工廠模式(Factory Pattern)是一種創建型設計模式,它提供了一種在不指定具體類的情況下創建對象的接口。 工廠模式的核心思想是,將對象的創建過程抽象出來,使得使用者的創建與類的實例化過程解耦。 在工廠模式中,會實現一個工廠類,它提供一個創建對象的接口,而使用者只需要調用這個接口即可,工廠類會根據客戶端的請求,返回不同類型的對象。 |
threadFactory?具體指什么:
ThreadFactory threadFactory?線程工廠 | |
通過工廠類創建線程對象(簡單理解就是通過這個類產出線程)。ThreadFactory 是一個接口,接口中只中有一個方法 newThread 方法,通過覆寫該方法獲得一個線程,同時可以給這個新線程設置一些屬性。 |
2.1.5?handler
RejectedExecutionHandler handler?拒絕策略 | |
線程池異常處理器,也稱拒絕策略。這里的 handler 表示一個實現了 RejectedExecutionHandler 接口的實現類,傳入的 handler 參數決定了出現異常時,線程池如何處理異常。 |
有哪幾種拒絕策略,分別表示什么? | ||||
ThreadPoolExecutor.AbortPolicy | 當線程池滿時,如果新任務無法執行,則立即拋出 RejectedExecutionException 異常,是默認策略。 | |||
ThreadPoolExecutor.CallerRunspolicy | 當線程池滿時,如果新任務無法執行,則由調用線程自己執行新任務。 | |||
ThreadPoolExecutor.DiscardOldestPolicy | 當線程池滿時,如果新任務無法執行,則丟棄最早添加的任務,然后執行新任務。 | |||
ThreadPoolExecutor.DiscardPolicy | 當線程池滿時,如果新任務無法執行,則丟棄新任務,不執行。 |
2.1.6 創建一個參數自定義的線程池
創建并運行以下線程池和任務:
分析上述代碼運行結果:
2.2?Executors 類
1)簡單介紹 Executors 類 | |
ThreadPoolExecutor 類參數較多,使用較復雜。因此,為方便使用,標準庫中又提供了?Executors 類。Executors 類是對?ThreadPoolExecutor 類進行了一層封裝,是一個工廠類,通過這個類創建出不同屬性的線程池對象。 |
2)簡單介紹常用的 Executors 類創建線程池的方法 | |||
newFixedThreadPool | 創建固定線程數的線程池 | ||
newCachedThreadPool | 創建線程數目動態增長的線程池 | ||
newSingleThreadExecutor | 創建只包含單個線程的線程池 | ||
newScheduledThreadPool | 創建可以定時執行任務的線程池 |
3)開發過程中應該使用?ThreadPoolExecutor 類還是使用 Executors 類? | |
ThreadPoolExecutor 類參數較多、可以自定義,這意味著使用這個類創建的線程池更靈活。 而?Executors 類相比 ThreadPoolExecutor 類多了一層封裝,部分參數已經設定好,這使得?Executors 類在使用上更便利。 這兩種選擇,前者更偏向于高度定制化的線程池,而后者偏向于通用性。兩者并無高下之分,各有偏向,使用者根據實際應用場景使用即可。 |
2.3 實現自己的線程池
線程池的使用有以下關鍵點: | ||
<1> | 線程池,肯定要有線程。在這里實現一個線程數量固定的線程池,就需要在這個類的構造方法中,根據輸入的參數 n ,新建 n 個線程。 | |
<2> | 維護一個阻塞隊列,隊列持有需要執行的任務。 | |
<3> | 提供一個 submit 方法,用于將任務添加到任務隊列中。 | |
<4> | 線程池中的線程,也有自己的任務,就是不斷地掃描任務隊列,如果隊列中有任務,則取出任務后執行。 | |
<5> | 注意新建的線程需要有合適的啟動時機。在以下實現中,我們讓線程一創建就啟動。 |
代碼演示線程池的實現:
class MyThreadPool{//創建一個用于存放任務的隊列;private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(5);//構造方法,用于構造線程池。方法中需要將線程new出來;public MyThreadPool(int n){//設置線程需要執行的任務;Runnable runnable = new Runnable() {@Overridepublic void run() {while (true){try {//從任務隊列中取出任務,并執行;queue.take().run();} catch (InterruptedException e) {e.printStackTrace();}}}};//循環創建線程,并啟動,將線程放入隊列中;for (int i = 0; i < n; i++){Thread t = new Thread(runnable);t.start();}}//添加任務方法。往任務隊列中添加任務;public void submit(Runnable runnable) throws InterruptedException {queue.put(runnable);}
}
public class ThreadPool_Demo35 {public static void main(String[] args) throws InterruptedException {//新建線程池;MyThreadPool threadPool = new MyThreadPool(2);//給線程池添加任務;for (int i= 0; i < 10; i++){int n = i;threadPool.submit(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " 執行任務:" + n);}});}}
}//運行結果:
Thread-0 執行任務:0
Thread-1 執行任務:1
Thread-0 執行任務:2
Thread-1 執行任務:3
Thread-0 執行任務:4
Thread-0 執行任務:6
Thread-0 執行任務:7
Thread-0 執行任務:8
Thread-0 執行任務:9
Thread-1 執行任務:5
...十個任務全部成功打印。
應該注意到,線程沒有執行結束,還在等待新的任務加入,這是線程池的合理功能。
閱讀指針 -> 《 Synchronized 鎖進階》
鏈接生成中..........