快速 Get 面试题中:线程池七个参数和工作原理问题,理解万岁
這次用一個生活案例來讓你快速的 get 到線程池的工作原理和七個參數的作用。
最近在復習面試題,鞏固一下自己。學完看看面試題,可以很有效的知道的不足。也是為以后做準備。
你好,我是博主寧在春,希望文章能夠讓你有所收獲,也讓我們也一起努力!!!
本文主要針對線程池的七個參數及工作原理做講解。
閱讀完本文能夠通過自己的語言簡單闡述線程池的工作原理和畫出原理圖。
一、面試題
在看很多Java面試題相關的文章或者博客中,對于線程池都會有這么幾個常見連環問題。
👨?💻面試官:
問題,都是一步一步深入。
我們在回答的時候,要盡可能的讓面試官往自己更加有把握的地方問過去。
二、線程池的七個參數
我們平常在使用線程池時,不是使用Executors工具類來創建的,而是顯式的使用ThreadPoolExecutor來創建的。
原因:
【強制】線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程。 說明:線程池的好處是減少在創建和銷毀線程上所消耗的時間以及系統資源的開銷,解決資源不足的問題。 如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或者“過度切換”的問題。
【強制】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這 樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。 說明:Executors 返回的線程池對象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool: 允許的請求隊列長度為 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。
2) CachedThreadPool: 允許的創建線程數量為 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。
? 來自于阿里的Java開發手冊
接下來來看我們今天的重點哈:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {}一個一個參數分析:
corePoolSize– 核心線程數。要保留在池中的線程數,即使它們處于空閑狀態,除非設置了allowCoreThreadTimeOut
maximumPoolSize– 池中能夠容納同時執行的最大線程數,此值必須大于等于1.
keepAliveTime– 當線程數大于核心數時,這是多余空閑線程在終止前等待新任務的最長時間。
當前線程池數量超過corePoolSize時,當空閑時間達到KeepAliveTime值時,多余空閑線程會被銷毀直到只剩下corePoolSize個線程為止。
unit– keepAliveTime參數的時間單位
workQueue– 用于在執行任務之前保存任務的隊列。 這個隊列將只保存execute方法提交的Runnable任務。
threadFactory– 執行程序創建新線程時使用的工廠 ,一般用默認即可。
handler– 執行被阻塞時使用的處理程序,因為達到了線程邊界和隊列容量。平常稱為拒絕策略。
看完這個概念一下肯定是沒啥感覺的,我會用一個生活中的案列來講解線程池的工作原理,確保大家都能理解。
三、線程池的工作原理
3.1、原理圖
先說說這張圖:
1)當調用 execute()方法添加一個請求任務時,線程池會做出如下判斷:
- 如果正在運行的線程數量小于 corePoolSize,那么馬上創建線程運行這個任務;
- 如果正在運行的線程數量大于或等于 corePoolSize,那么將這個任務放入阻塞隊列中,進行排隊等待;
- 如果這個時候隊列滿了且正在運行的線程數量還小于 maximumPoolSize,那么還是要創建非核心線程立刻運行這個任務;
- 如果隊列滿了且正在運行的線程數量大于或等于 maximumPoolSize,那么線程池會啟動飽和拒絕策略來執行。
2)當一個線程完成任務時,它會從隊列中取下一個任務來執行
3)當一個線程無事可做超過一定的時間(keepAliveTime)時,線程會判斷:
4)拒絕策略,Jdk默認的拒絕策略有以下四種:
上面這個內容我第一次看的時候,也稍稍有些懵,學習的時候懂,過段時間再看又是陌生人啦。
這次為了讓記憶更為深刻,舉一個特別形象生動的例子。源自于 尚硅谷--周陽老師
3.2、生活案例
相信我們大家肯定都去過銀行哈。這次就是以銀行來舉例的。先看看場景
我來解釋下哈:
一名顧客來到了銀行,發現沒人在辦理業務,直接就到值班1號窗口開始辦理業務。
接著又來了一名顧客,2號窗口沒人,就走到了值班2號窗口開始辦理業務。
緊接著又來了第三名、第四名、第五名顧客,現在值班1、2號窗口都正在辦理業務,他們就坐在等候區開始等待。
但是這時候又來了第六名、第七名、第八名顧客。這時候,等候區已滿,大堂經理看到這個情況,馬上告訴行長,需要增加臨時辦理窗口。
問題來了?這個時候增添的臨時窗口3、4、5會讓誰先去辦理業務呢???
是后來的第六、七、八名顧客,還是之前就在等候區的第三、四、五名顧客呢??
會處理剛進來的第六、七、八名顧客。
原因:在上文我說過,當調用 execute()方法添加一個請求任務時,線程池會做出如下判斷:
如果這個時候隊列滿了且正在運行的線程數量還小于 maximumPoolSize,那么還是要創建非核心線程立刻運行這個任務;所以會立刻處理第6、7、8名顧客業務。
繼續,如果接著還有很多顧客過來處理業務,這時候大堂經理看到這種情況,就觸發拒絕策略。(這種情況就是陌生的哈,那個經理敢把顧客往外趕哈)
接下來我們用代碼來模擬這個場景哈。
四、通過代碼示例來進一步分析參數
設置的數據就按照圖上的來,
corePoolSize核心線程數為:2,maximumPoolSize最大線程數為:5,keepAliveTime:等待時間為:3秒
workQueue阻塞隊列為:3,ThreadFactory線程的創建方式就使用默認的:Executors.defaultThreadFactory()
RejectedExecutionHandler拒絕策略:都會測試一遍。
public static void main(String[] args) {BlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<Runnable>(3);ThreadPoolExecutor executor = new ThreadPoolExecutor(2,5,3, TimeUnit.SECONDS,workQueue,Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i <8 ; i++) {final int temp=i;executor.execute(()->{System.out.println(Thread.currentThread().getName()+"\t"+"執行第 "+temp+" 個任務!!");});}}這個代碼里設置的拒絕策略為:new ThreadPoolExecutor.AbortPolicy(),超過maximumPoolSize+workQueue之和的數據。就會直接拋出異常,停止運行。
當設置為8的時候,還是可以正常的,我們調到9個任務來試一試。
當我們向上調整上,任務超過最大數,就會觸發拒絕策略。
將策略改為new ThreadPoolExecutor.DiscardPolicy());直接就拋棄了第八個任務。
策略改為:new ThreadPoolExecutor.CallerRunsPolicy(),當觸發拒絕策略,只要線程池沒有關閉的話,則使用調用線程直接運行任務。一般并發比較小,性能要求不高,不允許失敗。
還有最后一個DiscardOldestPolicy: 觸發拒絕策略時,只要線程池沒有關閉的話,丟棄阻塞隊列 workQueue中最老的任務,并將新任務加入
五、自言自語
你好,我是博主寧在春:主頁
希望本篇文章能讓你感到有所收獲!!!
祝 我們:待別日相見時,都已有所成。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的快速 Get 面试题中:线程池七个参数和工作原理问题,理解万岁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「后端小伙伴来学前端了」Vue-Rout
- 下一篇: Java设计模式-中介者模式