問題:請講下java中的線程池
分析:在面試中經常問到線程池的問題,要掌握其基本概念,使用方法,注意事項等,引申下tomcat中默認的線程數是多少
回答要點:
主要從以下幾點去考慮,
1、為什么要使用線程池
2、線程池的基本參數
3、為什么不使用java提供的線程池,而是使用自己創建
4、如何設置線程數大小;
5、線程池在tomcat中的使用;
為什么要使用線程池
在日常的開發過程中,經常要用到多線程,那么為什么不直接新建一個線程,而是選擇使用線程池那,因為線程的創建要消耗系統資源,占用CPU的時間,所以考慮使用線程池;
線程池的基本參數
Java提供了線程池類ThreadPoolExecutor,該類是線程池的基類,有以下參數
corePoolSize 核心線程數
maximumPoolSize 最大線程數
keepAliveTime 線程數超過核心線程數后,多余的線程的空閑時間
unit 上面的參數的單位
workQueue 阻塞隊列
threadFactory 創建線程的工廠
handler 拒絕策略
一個任務被提交到線程池的過程:
從上面的流程圖中可以引申出以下幾個問題,核心線程數要怎么設置;阻塞隊列有幾種;拒絕策略有幾種;
先看阻塞隊列及決策策略
阻塞隊列
常用的阻塞隊列有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、priorityBlockingQueue
ArrayBlockingQueue 底層使用數組+ReeTrantLock實現,既然是數組實現那么必然就要指定數組的長度
LinkedBlockingQueue 使用單鏈表實現,最大長度為Integer.MAX_VALUE
SynchronousQueue 要將任務放入SynchronousQueue中必須有一個線程在等待消費該任務,如果沒有線程在等待,那么線程池中線程數小於最大線程數就會創建一個線程,否則會執行拒絕策略
PriorityBlockingQueue 具有優先級的阻塞隊列
拒絕策略
ThreadPoolExecutor實現了4中拒絕策略,CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy
CallerRunsPolicy 調用者來執行該任務;
AbortPolicy 拋出異常,開發者可以捕獲該異常,默認的策略;
DiscardPolicy 直接丟棄,什么也不做;
DiscardOldestPolicy 丟棄隊列中最老的任務,就是最先入隊的任務,也即將要被執行的任務;
java提供的線程池
Java的Exectors類提供了一些線程池的方法供開發者來用,但是最好不用Java提供的,下面看有哪些以及為什么不建議使用
newFixedThreadPool() 固定線程數的線程池,核心線程數=最大線程數,使用LinkedBlockingQueue,拒絕策略是默認的AbortPolicy;適用於為了滿足資源管理的要求,而限制線程數量,適用於負載較重的機器
newSingleThreadExecutor() 只有一個線程的線程池,核心線程數=最大線程數=1,使用LinkedBlockingQueue,拒絕策略是默認的AbortPolicy;適用於順序執行各個任務的場景
newCachedThreadPool() 按需創建新的線程池,核心線程數=0,最大線程數為Integer.MAX_VALUE,阻塞隊列為SynchronousQueue,拒絕策略是默認的AbortPolicy,線程池可以無限擴展,當任務多時創建許多線程,任務少時,自動清空線程;適應於執行短期異步任務,或者負載較輕的機器
newScheduledThreadPool() 創建一個延時或定時的線程池,最大線程數為Integer.MAX_VALUE,阻塞隊列為DelayedWorkQueue
如何設置線程池中線程的數量
要想設置合理的線程數,需要區分任務是計算(CPU)密集型還是IO密集型
計算密集型 針對計算密集型一般設置為CPU核數+1比較合理,因為是計算密集型,那么計算就要用CPU,設置為CPU的核數可以充分利用CPU的優勢,至於為什么要加1,可以理解為計算密集型,也要有IO操作,加1是為了在等待IO的時候,充分利用CPU
IO密集型 對應IO密集型,也就是說該任務涉及很多的IO操作,比如讀寫磁盤,遠程RPC調用等,網上有很多數是設置為2*CPU核數+1,當然這也是可以的,不過下面這樣設置可能更能發揮CPU的性能,CPU數*CPU利用率*(任務等待時間/任務計算時間+1),假如有個8核機器,任務有100ms在計算,800ms在等待IO,CPU的利用率為100%的話,線程數=8*1*(800/100+1)=72,當然現實場景中CPU的利用率不可能飆到100%,還要具體場景具體分析;
線程池在tomcat中的使用
tomcat作為servlet服務器,要處理請求,是為每個請求創建一個線程,那么它的配置是在哪里那,在tomcat的conf/server.xml文件中
看上面有個Executor的配置實例,maxThreads的配置,看上圖中例子給出的值為150。其實tomcat7/8默認的線程池大小為200.
參考:https://joonwhee.blog.csdn.net/article/details/106609583