Java線程池相關面試題


1.什么是線程池?

java.util.concurrent.Executors提供了一個 java.util.concurrent.Executor接口的實現用於創建線程池
假設一個服務器完成一項任務所需時間為:T1 創建線程時間,T2 在線程中執行任務的時間,T3 銷毀線程時間。
如果T1+T3遠大於T2,則可以采用線程池,以提高服務器的性能

一個線程池包括以下四個基本組成部分:
1、線程池管理器(ThreadPool):用於創建並管理線程池,包括 創建線程池,銷毀線程池,添加新任務;
2、工作線程(PoolWorker):線程池中線程,在沒有任務時處於等待狀態,可以循環的執行任務;
3、任務接口(Task):每個任務必須實現的接口,以供工作線程調度任務的執行,它主要規定了任務的入口,任務執行完后的收尾工作,任務的執行狀態等;
4、任務隊列(taskQueue):用於存放沒有處理的任務。提供一種緩沖機制。

線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高服務器程序性能的。它把T1,T3分別安排在服務器程序的啟動和結束的時間段或者一些空閑的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。
線程池不僅調整T1,T3產生的時間段,而且它還顯著減少了創建線程的數目,看一個例子:
假設一個服務器一天要處理50000個請求,並且每個請求需要一個單獨的線程完成。在線程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目,而如果服務器不利用線程池來處理這些請求則線程總數為50000。一般線程池大小是遠小於50000。所以利用線程池的服務器程序不會為了創建50000而在處理請求時浪費時間,從而提高效率。

2.常見的線程池有哪些?

//1.
newSingleThreadExecutor
單個線程的線程池,即線程池中每次只有一個線程工作,單線程串行執行任務
//2.
newFixedThreadExecutor(n)
固定數量的線程池,沒提交一個任務就是一個線程,直到達到線程池的最大數量,然后后面進入等待隊列直到前面的任務完成才繼續執行
//3.
newCacheThreadExecutor(推薦使用)
可緩存線程池,當線程池大小超過了處理任務所需的線程,那么就會回收部分空閑(一般是60秒無執行)的線程,當有任務來時,又智能的添加新線程來執行。
//4.
newScheduleThreadExecutor
大小無限制的線程池,支持定時和周期性的執行線程

Executors類的靜態方法 創建ExecutorService線程池的類型
newFixedThreadPool(int nThreads) 線程池包含固定數目的線程,空閑線程會一直保留.參數nThreads設定線程池線程的數目
newSingleThreadExecutor() 線程池只有一個工作線程,它依次執行任務
newCachedThreadPool() 在有任務時才創建新線程,空閑線程被保留60s
newSingleThreadScheduledExecutor() 線程池中只有一個工作線程,它按照時間計划來執行任務
newScheduledThreadPool(int corePoolSize) 線程池能按時間計划來執行任務,允許用戶設定計划執行任務的時間.參數corePoolSize設定線程池中線程的最小數目.當任務較多時,線程池可能會創建更多的工作線程來執行任務

要配置一個線程池是比較復雜的,尤其是對於線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優的,因此在Executors類里面提供了一些靜態工廠,生成一些常用的線程池。

2.1 newSingleThreadExecutor

創建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當於單線程串行執行所有任務。如果這個唯一的線程因為異常結束,那么會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。

2.2 newFixedThreadPool

創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那么線程池會補充一個新線程。

2.3 newCachedThreadPool

創建一個可緩存的線程池。如果線程池的大小超過了處理任務所需要的線程,

那么就會回收部分空閑(60秒不執行任務)的線程,當任務數增加時,此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。

2.4 newScheduledThreadPool

創建一個大小無限的線程池。此線程池支持定時以及周期性執行任務的需求。

3.為什么不建議使用 Executors靜態工廠構建線程池

阿里巴巴Java開發手冊,明確指出不允許使用Executors靜態工廠構建線程池
原因如下:
線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險

說明:Executors返回的線程池對象的弊端如下:
1:FixedThreadPool 和 SingleThreadPool:
允許的請求隊列(底層實現是LinkedBlockingQueue)長度為Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM(避免無限創建線程引起的OutOfMemoryError)
2:CachedThreadPool 和 ScheduledThreadPool
允許的創建線程數量為Integer.MAX_VALUE,可能會創建大量的線程,從而導致OOM。

創建線程池的正確姿勢
避免使用Executors創建線程池,主要是避免使用其中的默認實現,那么我們可以自己直接調用ThreadPoolExecutor的構造函數來自己創建線程池。在創建的同時,給BlockQueue指定容量就可以了。

private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue(10));

或者是使用開源類庫:開源類庫,如apache和guava等

3.線程池常用參數

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) { }

corePoolSize:核心線程數量,會一直存在,除非allowCoreThreadTimeOut設置為true
maximumPoolSize:線程池允許的最大線程池數量
keepAliveTime:線程數量超過corePoolSize,空閑線程的最大超時時間
unit:超時時間的單位
workQueue:工作隊列,保存未執行的Runnable 任務
threadFactory:創建線程的工廠類
handler:當線程已滿,工作隊列也滿了的時候,會被調用。被用來實現各種拒絕策略。

對網上大佬關於線程池的面試總結到這就告一段落了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM