更新時間:2022-12-27 10:30:15 來源:動力節點 瀏覽1268次
對于廣大程序員來說,線程池一定是不陌生的,線程池是一種多線程的處理形式,也是我們在參加Java開發面試中必考的知識點,也是重點的考核題目之一,為了能夠更順利的讓大家通過面試,小編特別整理了這些線程池經典問題,希望可以幫助到大家:
1. 為什么要用線程池? 不能直接new個線程嗎?
如果我們在方法中直接new一個線程來處理,當這個方法被調用頻繁時就會創建很多線程,不僅會消耗系統資源,還會降低系統的穩定性。
降低資源消耗。通過重復利用已創建的線程,降低線程創建和銷毀造成的消耗。
提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
增加線程的可管理型。線程是稀缺資源,使用線程池可以進行統一分配,調優和監控。
2. 線程池的核心屬性有哪些?
threadFactory(線程工廠):用于創建工作線程的工廠
corePoolSize(核心線程數):當線程池運行的線程少于 corePoolSize 時,將創建一個新線程來處理請求,即使其他工作線程處于空閑狀態
workQueue(隊列):用于保留任務并移交給工作線程的阻塞隊列
maximumPoolSize(最大線程數):線程池允許開啟的最大線程數
handler(拒絕策略):往線程池添加任務時,將在下面兩種情況觸發拒絕策略:1)線程池運行狀態不是 RUNNING;2)線程池已經達到最大線程數,并且阻塞隊列已滿時
keepAliveTime(保持存活時間):如果線程池當前線程數超過 corePoolSize,則多余的線程空閑時間超過 keepAliveTime 時會被終止
unit(空閑線程存活時間單位):keepAliveTime 的計量單位
3. 線程池的執行流程?
//線程池實現原理
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
// 1.?先判斷當前線程池中之?的任務數量是否小于 corePoolSize
// 如果小于的話,通過addWorker(command, true)新建?個線程,并將任務(command)
//添加到該線程中;然后,啟動該線程從?執?任務。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.如果當前執行的任務數量?于等于 corePoolSize 的時候就會?到這
// 通過 isRunning ?法判斷線程池狀態,線程池處于 RUNNING 狀態才會被阻塞隊列加?任務,該任務才會被加?進去
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 再次獲取線程池狀態,如果線程池狀態不是 RUNNING 狀態就需要從任務隊列中移除任務,并嘗試判斷線程是否全部執?完畢。同時執?拒絕策略。
if (! isRunning(recheck) && remove(command))
reject(command);
// 如果當前線程池為空就新創建?個線程并執?
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3. 通過addWorker(command, false)新建?個線程,
//并將任務(command)添加到該線程中;然后,啟動該線程從?執?任務。
//如果addWorker(command, false)執?失敗,則通過reject()執?相應的拒絕策略的內容。
else if (!addWorker(command, false))
reject(command);
}
4. Executors 提供了哪些創建線程池的方法?
newFixedThreadPool:固定線程數的線程池。corePoolSize = maximumPoolSize,keepAliveTime為0,工作隊列使用無界的LinkedBlockingQueue。適用于為了滿足資源管理的需求,而需要限制當前線程數量的場景,適用于負載比較重的服務器。
newSingleThreadExecutor:只有一個線程的線程池。corePoolSize = maximumPoolSize = 1,keepAliveTime為0, 工作隊列使用無界的LinkedBlockingQueue。適用于需要保證順序的執行各個任務的場景。
newCachedThreadPool: 按需要創建新線程的線程池。核心線程數為0,最大線程數為 Integer.MAX_VALUE,keepAliveTime為60秒,工作隊列使用同步移交 SynchronousQueue。該線程池可以無限擴展,當需求增加時,可以添加新的線程,而當需求降低時會自動回收空閑線程。適用于執行很多的短期異步任務,或者是負載較輕的服務器。
newScheduledThreadPool:創建一個以延遲或定時的方式來執行任務的線程池,工作隊列為 DelayedWorkQueue。適用于需要多個后臺線程執行周期任務。
newWorkStealingPool:JDK 1.8 新增,用于創建一個可以竊取的線程池,底層使用 ForkJoinPool 實現。
5. 使用隊列有什么需要注意的嗎?
使用有界隊列時,需要注意線程池滿了后,被拒絕的任務如何處理。
使用無界隊列時,需要注意如果任務的提交速度大于線程池的處理速度,可能會導致內存溢出。
6. 線程池有哪些拒絕策略?
AbortPolicy:中止策略。默認的拒絕策略,直接拋出 RejectedExecutionException。調用者可以捕獲這個異常,然后根據需求編寫自己的處理代碼。
DiscardPolicy:拋棄策略。什么都不做,直接拋棄被拒絕的任務。
DiscardOldestPolicy:拋棄最老策略。拋棄阻塞隊列中最老的任務,相當于就是隊列中下一個將要被執行的任務,然后重新提交被拒絕的任務。如果阻塞隊列是一個優先隊列,那么“拋棄最舊的”策略將導致拋棄優先級最高的任務,因此最好不要將該策略和優先級隊列放在一起使用。
CallerRunsPolicy:調用者運行策略。在調用者線程中執行該任務。該策略實現了一種調節機制,該策略既不會拋棄任務,也不會拋出異常,而是將任務回退到調用者(調用線程池執行任務的主線程),由于執行任務需要一定時間,因此主線程至少在一段時間內不能提交任務,從而使得線程池有時間來處理完正在執行的任務。
以上就是“2023年常見必考的線程池面試題”,你能回答上來嗎?如果想要了解更多的Java面試題相關內容,可以關注動力節點Java官網。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習