聊到線程池就一定會聊到線程池的執行流程,也就是當有一個任務進入線程池之后,線程池是如何執行的?我們今天就來聊聊這個話題。線程池是如何執行的?線程池的拒絕策略有哪些?
想要真正的了解線程池的執行流程,就得先從線程池的執行方法 execute() 說起,execute() 實現源碼如下:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); // 當前工作的線程數小于核心線程數 if (workerCountOf(c) < corePoolSize) { // 創建新的線程執行此任務 if (addWorker(command, true)) return; c = ctl.get(); } // 檢查線程池是否處于運行狀態,如果是則把任務添加到隊列 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); // 再次檢線程池是否處于運行狀態,防止在第一次校驗通過后線程池關閉 // 如果是非運行狀態,則將剛加入隊列的任務移除 if (! isRunning(recheck) && remove(command)) reject(command); // 如果線程池的線程數為 0 時(當 corePoolSize 設置為 0 時會發生) else if (workerCountOf(recheck) == 0) addWorker(null, false); // 新建線程執行任務 } // 核心線程都在忙且隊列都已爆滿,嘗試新啟動一個線程執行失敗 else if (!addWorker(command, false)) // 執行拒絕策略 reject(command);}
從上述源碼我們可以看出,當任務來了之后,線程池的執行流程是:先判斷當前線程數是否大于核心線程數?如果結果為 false,則新建線程并執行任務;如果結果為 true,則判斷任務隊列是否已滿?如果結果為 false,則把任務添加到任務隊列中等待線程執行,否則則判斷當前線程數量是否超過最大線程數?如果結果為 false,則新建線程執行此任務,否則將執行線程池的拒絕策略,如下圖所示:
當任務過多且線程池的任務隊列已滿時,此時就會執行線程池的拒絕策略,線程池的拒絕策略默認有以下 4 種:
默認的拒絕策略為 AbortPolicy 中止策略。
接下來我們以 DiscardPolicy 忽略此任務,忽略最新的一個任務為例,演示一下拒絕策略的具體使用,實現代碼如下:
public static void main(String[] args) { // 任務的具體方法 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("當前任務被執行,執行時間:" + new Date() + " 執行線程:" + Thread.currentThread().getName()); try { // 等待 1s TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }; // 創建線程,線程的任務隊列的長度為 1 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.DiscardPolicy()); // 添加并執行 4 個任務 threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); // 線程池執行完任務,關閉線程池 threadPool.shutdown();}
以上程序的執行結果如下:
從上述執行結果可以看出,給線程池添加了 4 個任務,而線程池只執行了 2 個任務就結束了,其他兩個任務執行了拒絕策略 DiscardPolicy 被忽略了,這就是拒絕策略的作用。
為了和 DiscardPolicy 拒絕策略對比,我們來演示一下 JDK 默認的拒絕策略 AbortPolicy 中止策略,線程池會拋出異常并中止執行此任務,示例代碼如下:
public static void main(String[] args) { // 任務的具體方法 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("當前任務被執行,執行時間:" + new Date() + " 執行線程:" + Thread.currentThread().getName()); try { // 等待 1s TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }; // 創建線程,線程的任務隊列的長度為 1 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.AbortPolicy()); // 顯式指定拒絕策略,也可以忽略此設置,它為默認拒絕策略 // 添加并執行 4 個任務 threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); // 線程池執行完任務,關閉線程池 threadPool.shutdown();}
以上程序的執行結果如下:
從結果可以看出,給線程池添加了 4 個任務,線程池正常執行了 2 個任務,其他兩個任務執行了中止策略,并拋出了拒絕執行的異常 RejectedExecutionException。
當然除了 JDK 提供的四種拒絕策略之外,我們還可以實現通過 new RejectedExecutionHandler,并重寫 rejectedExecution 方法來實現自定義拒絕策略,實現代碼如下:
public static void main(String[] args) { // 任務的具體方法 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("當前任務被執行,執行時間:" + new Date() + " 執行線程:" + Thread.currentThread().getName()); try { // 等待 1s TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }; // 創建線程,線程的任務隊列的長度為 1 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { // 執行自定義拒絕策略的相關操作 System.out.println("我是自定義拒絕策略~"); } }); // 添加并執行 4 個任務 threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable);}
以上程序的執行結果如下:
線程池的執行流程有 3 個重要的判斷點(判斷順序依次往后):判斷當前線程數和核心線程數、判斷當前任務隊列是否已滿、判斷當前線程數是否已達到最大線程數。如果經過以上 3 個判斷,得到的結果都會 true,則會執行線程池的拒絕策略。JDK 提供了 4 種拒絕策略,我們還可以通過 new RejectedExecutionHandler 并重寫 rejectedExecution 方法來實現自定義拒絕策略。
本文鏈接:http://www.www897cc.com/showinfo-26-6174-0.html面試必問:線程池是如何執行的?它的拒絕策略有哪些?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 六種在 React 中獲取數據的方法
下一篇: 揭穿DevOps的5個謠言!