身為程式設計師我們對執行緒是再熟悉不過了,多執行緒併發算是Java進階的知識,用好多執行緒不容易有太多的坑。建立執行緒也算是一個"重"操作。建立執行緒的語句是new Thread()
咋一看好像就是new了一個物件。
沒錯是new了個物件,但是不僅僅是普通物件那樣在堆中分配了一塊記憶體,它還需要呼叫作業系統核心API,然後作業系統再為執行緒分配一些資源。所以較普通物件,執行緒就比較“重了”。所以我們要避免頻繁的建立和銷燬執行緒,還得控制一下執行緒的數量。執行緒池就是用來完成這一項使命的。
所以多執行緒就離不開執行緒池,所以要掌握多執行緒程式設計,執行緒池的瞭解必不可少。
執行緒池的設計就是採用生產者-消費者模式,執行緒池裡面的執行緒是消費者,我們塞給執行緒池的任務是生產者。可以理解成執行緒池就是火車站售票廳,執行緒池裡面的執行緒就是火車站售票廳視窗員工,我們去買票或者退票改簽就是給視窗員工任務也就是生產,然後視窗員工幫我們辦理業務,也就是消費。
一般我們是用ThreadPoolExecutor
來建立執行緒池,我找了裡面引數最多的構造器。
1、corePoolSize
按字面翻譯過來就是核心池大小,其實就是執行緒池保證有的最小的執行緒數,也就是執行緒池最少有這麼個數量的執行緒在。也就是火車站售票廳視窗最少的開放數,總得有人在視窗的服務對吧,不能全部都休息。出行平淡期就開幾個視窗就夠消化這些業務了。 但是java1.6新增了一個allowCoreThreadTimeOut(boolean value)方法,當設為true時候,所有的執行緒都會超時回收,包括核心執行緒。
2、maximumPoolSize
最大執行緒數,也就是池裡面能有的最大的執行緒數量。也就是火車站售票廳視窗所有的視窗都有員工在服務。特別是在節假日的時候,基本上視窗都會開放。
3、keepAliveTime、TimeUnit
keepAliveTime就是存活時間,TimeUnit是時間單位,來表明keepAliveTime的數字是秒啊還是毫秒啊等等。 這兩個引數就是當我們執行緒池存在的執行緒數量超過corePoolSize時,如果有個執行緒已經空閒了keepAliveTime這麼長的時間,那麼這個空閒執行緒就要被回收了,就類似於出行高峰期過去了,售票廳視窗可以關閉幾個了。總不能都沒人了還開這麼多視窗把,浪費呀。
4、workQueue
工作佇列,也就是執行緒需要執行的Runnable,也就是任務。對應著就是去售票廳排隊的我們。
5、threadFactory
按名字翻譯過來就是執行緒工廠了,也就是我們可以搞個工廠,然後自定義如何建立執行緒,比如給執行緒set下名字啊等。然後執行緒池就會按照工廠定義的方式建立執行緒。就是如果不設定執行緒的名字的話,執行緒名可能就是什麼thread-1這樣的,對於我們排查問題不太方便,所以給個名字來標識一下比較好。
6、handler
這個是拒絕策略,也就是當執行緒池中所有的執行緒都在執行任務,並且工作佇列(是有界佇列)也排滿了,那再有任務提交就會執行拒絕策略。ThreadPoolExecutor提供了四種拒絕策略
1、ThreadPoolExecutor.AbortPolicy()
是預設的拒絕策略,會丟擲 RejectedExcecutionException。
2、ThreadPoolExecutor.CallerRunsPolicy()
讓提交任務的執行緒自己去執行這個任務。。好像這樣做挺有道理的..我沒空你自己搞去
3、ThreadPoolExecutor.DiscardOldestPolicy()
丟棄最老的任務,也就是工作佇列裡最前面的任務,丟棄了之後把新任務加入到工作佇列中...真的不公平啊
4、ThreadPoolExecutor.DiscardPolicy()
直接丟棄任務,並且不丟擲任何異常...假裝沒看到系列
除了這四種還可以自定義拒絕策略,建議自定義拒絕策略。因為更加的友好,可以設定成服務降級啊等操作。
注意
Java併發包還提供了Executors
,可以快速建立執行緒池,但是不推薦使用Executors
。因為Executors建立執行緒池都是預設使用無界佇列LinkedBlockingQueue
,在高負載的情況下容易OOM。所以建議使用有界佇列。
總結
所以執行緒池就是生產者-消費者模型的實現,執行緒池約束了執行緒的數量,也避免頻繁的建立和銷燬執行緒。工作佇列得存在使得任務有序的進行,完美!
如有錯誤歡迎指正! 個人公眾號:yes的練級攻略