- Offer來了:Java面試核心知識點精講(原理篇)
- 王磊
- 1943字
- 2020-04-03 12:50:11
3.2 線程池的工作原理
Java線程池主要用于管理線程組及其運行狀態,以便Java虛擬機更好地利用CPU資源。Java線程池的工作原理為:JVM先根據用戶的參數創建一定數量的可運行的線程任務,并將其放入隊列中,在線程創建后啟動這些任務,如果線程數量超過了最大線程數量(用戶設置的線程池大?。?,則超出數量的線程排隊等候,在有任務執行完畢后,線程池調度器會發現有可用的線程,進而再次從隊列中取出任務并執行。
線程池的主要作用是線程復用、線程資源管理、控制操作系統的最大并發數,以保證系統高效(通過線程資源復用實現)且安全(通過控制最大線程并發數實現)地運行。
3.2.1 線程復用
在Java中,每個Thread類都有一個start方法。在程序調用start方法啟動線程時,Java虛擬機會調用該類的run方法。前面說過,在Thread類的run方法中其實調用了Runnable對象的run方法,因此可以繼承Thread類,在start方法中不斷循環調用傳遞進來的Runnable對象,程序就會不斷執行run方法中的代碼??梢詫⒃谘h方法中不斷獲取的Runnable對象存放在Queue中,當前線程在獲取下一個Runnable對象之前可以是阻塞的,這樣既能有效控制正在執行的線程個數,也能保證系統中正在等待執行的其他線程有序執行。這樣就簡單實現了一個線程池,達到了線程復用的效果。
3.2.2 線程池的核心組件和核心類
Java線程池主要由以下4個核心組件組成。
◎ 線程池管理器:用于創建并管理線程池。
◎ 工作線程:線程池中執行具體任務的線程。
◎ 任務接口:用于定義工作線程的調度和執行策略,只有線程實現了該接口,線程中的任務才能夠被線程池調度。
◎ 任務隊列:存放待處理的任務,新的任務將會不斷被加入隊列中,執行完成的任務將被從隊列中移除。
Java中的線程池是通過Executor框架實現的,在該框架中用到了Executor、Executors、ExecutorService、ThreadPoolExecutor、Callable、Future、FutureTask這幾個核心類,具體的繼承關系如圖3-2所示。

圖3-2
其中,ThreadPoolExecutor是構建線程的核心方法,該方法的定義如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
ThreadPoolExecutor構造函數的具體參數如表3-1所示。
表3-1

3.2.3 Java線程池的工作流程
Java線程池的工作流程為:線程池剛被創建時,只是向系統申請一個用于執行線程隊列和管理線程池的線程資源。在調用execute()添加一個任務時,線程池會按照以下流程執行任務。
◎ 如果正在運行的線程數量少于corePoolSize(用戶定義的核心線程數),線程池就會立刻創建線程并執行該線程任務。
◎ 如果正在運行的線程數量大于等于corePoolSize,該任務就將被放入阻塞隊列中。
◎ 在阻塞隊列已滿且正在運行的線程數量少于maximumPoolSize時,線程池會創建非核心線程立刻執行該線程任務。
◎ 在阻塞隊列已滿且正在運行的線程數量大于等于maximumPoolSize時,線程池將拒絕執行該線程任務并拋出RejectExecutionException異常。
◎ 在線程任務執行完畢后,該任務將被從線程池隊列中移除,線程池將從隊列中取下一個線程任務繼續執行。
◎ 在線程處于空閑狀態的時間超過keepAliveTime時間時,正在運行的線程數量超過corePoolSize,該線程將會被認定為空閑線程并停止。因此在線程池中所有線程任務都執行完畢后,線程池會收縮到corePoolSize大小。
具體的流程如圖3-3所示。

圖3-3
3.2.4 線程池的拒絕策略
若線程池中的核心線程數被用完且阻塞隊列已排滿,則此時線程池的線程資源已耗盡,線程池將沒有足夠的線程資源執行新的任務。為了保證操作系統的安全,線程池將通過拒絕策略處理新添加的線程任務。JDK內置的拒絕策略有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy這4種,默認的拒絕策略在ThreadPoolExecutor中作為內部類提供。在默認的拒絕策略不能滿足應用的需求時,可以自定義拒絕策略。
1.AbortPolicy
AbortPolicy直接拋出異常,阻止線程正常運行,具體的JDK源碼如下:
public static class AbortPolicy implements RejectedExecutionHandler { public AbortPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { //直接拋出異常信息,不做任何處理 throw new RejectedExecutionException("Task " + r.toString() + " rejected from " +e.toString()); } }
2.CallerRunsPolicy
CallerRunsPolicy的拒絕策略為:如果被丟棄的線程任務未關閉,則執行該線程任務。注意,CallerRunsPolicy拒絕策略不會真的丟棄任務。具體的JDK實現源碼如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (! e.isShutdown()) { r.run(); //執行被丟棄的任務r } }
3.DiscardOldestPolicy
DiscardOldestPolicy的拒絕策略為:移除線程隊列中最早的一個線程任務,并嘗試提交當前任務。具體的JDK實現源碼如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (! e.isShutdown()) { e.getQueue().poll(); //丟棄(移除)線程隊列中最老(最后)的一個線程任務 e.execute(r); //嘗試提交當前任務 } }
4.DiscardPolicy
DiscardPolicy的拒絕策略為:丟棄當前的線程任務而不做任何處理。如果系統允許在資源不足的情況下丟棄部分任務,則這將是保障系統安全、穩定的一種很好的方案。具體的JDK實現源碼如下:
//直接丟棄線程,不做任何處理 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { }
5.自定義拒絕策略
以上4種拒絕策略均實現了RejectedExecutionHandler接口,若無法滿足實際需要,則用戶可以自己擴展RejectedExecutionHandler接口來實現拒絕策略,并捕獲異常來實現自定義拒絕策略。下面實現一個自定義拒絕策略DiscardOldestNPolicy,該策略根據傳入的參數丟棄最老的N個線程,以便在出現異常時釋放更多的資源,保障后續線程任務整體、穩定地運行。具體的JDK實現源碼如下:
public class DiscardOldestNPolicy implements RejectedExecutionHandler { private int discardNumber = 5; private List<Runnable> discardList =new ArrayList<Runnable>(); public DiscardOldestNPolicy (int discardNumber) { this.discardNumber = discardNumber; } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if(e.getQueue().size() > discardNumber){ //step 1:批量移除線程隊列中的discardNumber個線程任務 e.getQueue().drainTo(discardList, discardNumber); discardList.clear(); //step 2:清空discardList列表 if (! e.isShutdown()) { e.execute(r); //step 3:嘗試提交當前任務 } } } }
- UI圖標創意設計
- Mastering JavaScript Functional Programming
- Koa開發:入門、進階與實戰
- Visual Basic程序設計習題解答與上機指導
- Visual Basic程序設計與應用實踐教程
- PHP+MySQL+Dreamweaver動態網站開發實例教程
- Kubernetes源碼剖析
- C指針原理揭秘:基于底層實現機制
- Python趣味編程與精彩實例
- Unity 5.X從入門到精通
- Android高級開發實戰:UI、NDK與安全
- Java服務端研發知識圖譜
- 軟件測試項目實戰之功能測試篇
- 接口自動化測試持續集成:Postman+Newman+Git+Jenkins+釘釘
- μC/OS-III源碼分析筆記