大家好,我是小黑,一個在網際網路苟且偷生的農民工。
線程池是在計算機開發中常見的一種池化技術,是為了提高資源的使用率,将一些資源重複利用,避免重複的建構來提高效率。類似字元串常量池,資料庫連接配接池,HttpClient連接配接池等,都是用的池化技術。
在沒有線程池概念之前,我們要使用線程必須先通過建立一個Thread類來完成線程的建構,并調用<code>start()</code>方法開啟,線上程執行完會将線程銷毀,而線程資源是很寶貴的,建立和銷毀線程會造成資源的浪費。而線程池是将建立的線程存儲到一個池中,在需要使用時從池中去拿,使用完之後再講線程歸還到池中,下一次接着使用。
舉個栗子,好比我們去銀行辦理業務時,銀行會有視窗為客戶辦理業務,如果沒有線程池,就好比每次來一個客戶,銀行都打開一個視窗,辦理完業務之後将視窗關閉,這樣确實很浪費時間,是以銀行會預設開幾個視窗,比如三個,等客戶來辦理業務;一個客戶辦理完,下一個客戶可以繼續在這個視窗辦理。

線上程池初始化時,會指定建立核心線程的數量,有任務送出給線程池時,先判斷是否有空閑線程,如果有空閑線程,則直接使用,如果沒有則看目前線程池中的數量是不是小于核心線程數,如果是則建立新的線程,如果已經到達核心線程數,則需要做下一步操作。
下一步操作就是要進入等待隊列,等待隊列好比是去銀行辦業務時沒有空閑視窗,需要坐在大廳的座椅上排隊;線程池也是一樣,如果沒有核心線程,則需要将任務放入等待隊列,等待有空閑線程再執行。
那我們都知道銀行有時候人特别多的時候,會增加視窗,一般是當大廳的座椅坐滿人時,視窗都很緊張處理不過來,這是會增加視窗;線程池也是一樣,如果等待的任務已經放滿了等待隊列,并且核心線程都在繁忙,這時會檢視線程池中的線程數量是否到達最大線程數,如果沒有則會建立新的線程來繼續處理。
那如果說,線程池中的線程已經到達最大線程數并且都在繁忙,還有新的任務進來,好比銀行已經坐滿人了,視窗也都在忙,客戶都排到門口了,這時要是還有人要辦理,應該怎麼處理呢?銀行一般會讓這個人先回家,改天再來辦,或者如果是個大客戶,銀行可能會單獨帶去VIP辦公室辦理等等。線程池在這種情況下也有相應的處理方式,這種處理方式我們稱之為拒絕政策,如果會放任務在目前線程執行,或者直接将任務丢棄等等,在後面的章節中我會詳細給大家介紹。
在JDK中提供了相應的API來建立線程池,這些API也是在我們最近講到過的JUC包中。
首先,線程池需要具備能夠執行任務的能力,這個任務通過一個線程來處理。而這個執行任務的能力通過<code>Executor</code>接口來約定。
在某些場景我們需要知道任務執行完之後的結果,拿到傳回值,而<code>Runnable</code>接口是沒有傳回值的;以及一些可以關閉線程池中的線程,執行線程中的任務的方法,定義在<code>ExecutorService</code>接口中。
<code>ThreaPoolExecutor</code>則是對<code>ExecutorService</code>的具體實作類,通過<code>ThreaPoolExecutor</code>類可以建立出一個線程池,我們先來看一下代碼。
從代碼我們可以看出,建立一個線程池,需要指定我們上面說到的核心線程數,最大線程數,等待隊列,拒絕政策等,并且還要指定建立線程的工廠對象。
當然ThreadPoolExecutor也有其他的構造方法,可以不顯式指定拒絕政策和工廠對象。
corePoolSize:核心線程數
maximumPoolSize:最大線程數
keepAliveTime:空閑線程保持存活時間
unit:空閑線程保持存活時間機關
workQueue:等待隊列
threadFactory:線程建立工廠
RejectedExecutionHandler:拒絕政策
這裡我們說一下4中拒絕政策。接口<code>RejectedExecutionHandler</code>定義了拒絕政策,所有的拒絕政策都需要實作該接口。
在ThreadPoolExecutor類中定義了4個拒絕政策的具體實作。
AbortPolicy:拒絕處理,抛出異常
CallerRunsPolicy:由建立該線程的線程(main)執行
DiscardPolicy: 丢棄,不抛出異常
DiscardOldestPolicy:和最早建立的線程進行競争,不抛出異常
可通過如下方式進行拒絕政策的建立。
那麼具體我們在使用時應該建立怎樣的線程池呢?在JDK的<code>Executors</code>工具類為我們提供了4種線程池的建立方式。
好的,通過今天的内容我們先對線程池的使用有一個初步的了解,下期内容再跟大家深入解析一下線程池中的具體實作原理。
本期内容就到這裡,我們下期見。
關注公衆号【小黑說Java】幹貨不斷。