以下是我整理的一些java面試中常見的線程池問題,現在分享給大家。
(學習影片分享:java教學影片)
什麼是執行緒池?
執行緒池是一種多執行緒處理形式,處理過程中將任務提交到執行緒池,任務的執行交由執行緒池來管理。
如果每個請求都建立一個執行緒去處理,那麼伺服器的資源很快就會被耗盡,使用執行緒池可以減少建立和銷毀執行緒的次數,每個工作執行緒都可以被重複利用,可執行多個任務。
為什麼要使用執行緒池?
建立執行緒和銷毀執行緒的花銷是比較大的,這些時間有可能比處理業務的時間還要長。這樣頻繁的創建線程和銷毀線程,再加上業務工作線程,消耗系統資源的時間,可能導致系統資源不足。 (我們可以把創建和銷毀的線程的過程去掉)
線程池有什麼作用?
執行緒池作用就是限制系統中執行緒的數量。
1、提高效率 創建好一定數量的執行緒放在池中,等需要使用的時候就從池中拿一個,這要比需要的時候創建一個線程物件要快的多。
2、方便管理可以編寫線程池管理程式碼對池中的線程同一進行管理,比如說啟動時有該程式創建100個線程,每當有請求的時候,就分配一個線程去工作,如果剛好並發有101個請求,那多出的這一個請求可以排隊等候,避免因無休止的創建線程導致系統崩潰。
說說幾個常見的執行緒池及使用場景
1、newSingleThreadExecutor
建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務依照指定順序(FIFO, LIFO, 優先權)執行。
2、newFixedThreadPool
建立一個定長執行緒池,可控制執行緒最大並發數,超出的執行緒會在佇列中等待。
3、newCachedThreadPool
建立一個可快取線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
4、newScheduledThreadPool
建立一個定長執行緒池,支援定時及週期性任務執行。
線程池中的幾個重要的參數
corePoolSize就是線程池中的核心線程數量,這幾個核心線程,只是在沒有用的時候,也不會被回收
maximumPoolSize就是在執行緒池中可以容納的最大執行緒的數量
keepAliveTime,就是在執行緒池中除了核心執行緒之外的其他的最長可以保留的時間,因為在執行緒池中,除了核心執行緒即使在無任務的情況下也不能被清除,其餘的都是有存活時間的,意思就是非核心執行緒可以保留的最長的空閒時間
util,就是計算這個時間的一個單位。
workQueue,就是等待佇列,任務可以儲存在任務佇列中等待被執行,執行的是FIFIO原則(先進先出)。
(更多相關面試題分享:java面試題目及答案)
threadFactory,就是建立執行緒的執行緒工廠。
handler,是一種拒絕策略,我們可以在任務滿了之後,拒絕執行某些任務。
說說線程池的拒絕策略
當請求任務不斷的過來,而係統此時又處理不過來的時候,我們需要採取的策略是拒絕服務。 RejectedExecutionHandler介面提供了拒絕任務處理的自訂方法的機會。在ThreadPoolExecutor中已經包含四種處理策略。
AbortPolicy策略:此策略會直接拋出異常,阻止系統正常運作。
CallerRunsPolicy 策略:只要執行緒池未關閉,該策略直接在呼叫者執行緒中,執行目前的被丟棄的任務。
DiscardOleddestPolicy策略: 此策略將丟棄最老的一個請求,也就是即將被執行的任務,並嘗試再次提交目前任務。
DiscardPolicy策略:此策略默默的丟棄無法處理的任務,不予任何處理。
除了JDK預設提供的四種拒絕策略,我們可以根據自己的業務需求去自訂拒絕策略,自訂的方式很簡單,直接實作RejectedExecutionHandler介面即可。
execute和submit的差別?
在前面的講解中,我們執行任務是用的execute方法,除了execute方法,還有一個submit方法也可以執行我們提交的任務。
這兩個方法有什麼差別呢?分別適用於在什麼場景下呢?我們來做一個簡單的分析。
execute適用於不需要關注回傳值的場景,只需要將執行緒丟到執行緒池中去執行就可以了。
submit方法適用於需要關注傳回值的場景
五種執行緒池的使用場景
newSingleThreadExecutor:一個單執行緒的執行緒池,可以用來保證順序執行的場景,而且只有一個執行緒在執行。
newFixedThreadPool:一個固定大小的執行緒池,可以用來已知並發壓力的情況下,對執行緒數做限制。
newCachedThreadPool:一個可以無限擴大的執行緒池,比較適合處理執行時間比較小的任務。
newScheduledThreadPool:可以延時啟動,定時啟動的執行緒池,適用於需要多個後台執行緒執行週期任務的場景。
newWorkStealingPool:一個擁有多個任務佇列的執行緒池,可以減少連線數,並建立目前可用cpu數量的執行緒來並行執行。
執行緒池的關閉
關閉執行緒池可以呼叫shutdownNow和shutdown兩個方法來實現
shutdownNow:對正在執行的任務全部發出interrupt(),停止執行,對尚未開始執行的任務全部取消,並且傳回還沒開始的任務清單。
shutdown:當我們呼叫shutdown後,執行緒池將不再接受新的任務,但也不會去強制終止已經提交或正在執行中的任務。
初始化執行緒池時執行緒數的選擇
如果任務是IO密集型,一般執行緒數需要設定2倍CPU數以上,以此來盡量利用CPU資源。
如果任務是CPU密集型,一般執行緒數只需要設定CPU數加1即可,更多的執行緒數也只能增加上下文切換,不能增加CPU利用率。
上述只是一個基本思想,如果真的需要精確的控制,還是需要上線以後觀察線程池中線程數量跟隊列的情況來定。
執行緒池都有哪幾個工作佇列
1、ArrayBlockingQueue
是一個基於陣列結構的有界阻塞佇列,此佇列依FIFO(進階先出)原則對元素進行排序。
2、LinkedBlockingQueue
一個基於鍊錶結構的阻塞佇列,此佇列依FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue。靜態工廠方法Executors.newFixedThreadPool()使用了這個佇列
3、SynchronousQueue
一個不儲存元素的阻塞佇列。每個插入操作必須等到另一個執行緒呼叫移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個佇列。
4、PriorityBlockingQueue
一個具有優先權的無限阻塞佇列。
相關課程推薦:java入門教學
以上是java面試之線程池的詳細內容。更多資訊請關注PHP中文網其他相關文章!