阻塞隊列模型和線程池

阻塞隊列模型介紹java

阻塞隊列模型和線程池息息相關,所以本篇博客先介紹阻塞隊列的相關知識。以下圖所示:
數組

wKiom1b2RQ2SNHWrAAAu0KDMMdQ915.png

首先咱們來講,什麼是Queue,而後在談什麼是BlockingQueue。
緩存

那麼什麼是Queue呢?一句話,就是一端進,另外一端出,這樣就造成了First In , First Out,即先進先出。而BlockingQueue只不過是在Queue的基礎上進行了2個附加操做而已:若是Queue空,那麼Out線程阻塞,若是Queue滿,那麼In線程阻塞。併發


理解了上面的Queue/BlockingQueue,那麼就好理解Deque/BlockingDeque了。ide

Deque,就是雙端隊列,其實就是說在2端,均可以進行IN/OUT,固然若是咱們只在同一端進行IN/OUT,那麼天然造成了棧結構(先進後出)。高併發


其次,咱們先來看看java.util.concurrent.BlockingQueue的API列表:spa

wKiom1b2RsSBMEufAAAd4AxQv2M881.png

其實從這裏能夠看出JAVA API的一個思路,每每一個操做提供多種選擇:線程

若是隊列是空的,那麼消費線程該如何處理呢?3d

能夠馬上拋出異常,也能夠返回false/NULL,也能夠過一段時間在TRY...blog


好了,到這裏,咱們只看到了接口API,若是要你來實現,你會怎麼作呢?又會有哪些疑問呢?


思考:

是否應該提供一個存儲,來放置隊列中的元素呢?

這個存儲應該多大呢?能夠是什麼形式呢?

隊列中的元素是否存在優先級排序呢?

對隊列中的元素進行操做時,是否應該有鎖的控制呢?

一端IN,另外一端OUT,這2個操做能夠同時進行嗎?


帶着這些疑問,咱們來對典型的BlockingQueue來進行分析。


ArrayBlockingQueue PK LinkedBlockingQueue


存儲PK:

從名稱上就能夠知道,一個用的是數組,另外一個用的是鏈表。看看源碼驗證下:


ArrayBlockingQueue:

wKiom1b2TBqykUFqAAAHWCwuOfk038.png


LinkedBlockingQueue:

wKiom1b2TDGTfNZ7AAArcPBhXaU329.png


容量PK:

ArrayBlockingQueue能夠經過構造方法指定容量:

wKioL1b2TWqAkAZEAAAilXAs8XU931.png

而LinkedBlockingQueue若是在初始化時不指定容量,那麼將是Integer.MAX_VALUE,這一點很重要,特別是在生產者的速度大於消費者的速度,因爲此時無容量限制,將致使隊列中的元素開始膨脹,那麼將消耗掉大量系統資源。

wKioL1b2TiPjk5wWAABUl54WecY009.png


鎖PK:

在ArrayBlockingQueue中只有一個鎖:

wKiom1b2TdKAEXG_AAAJ8rP7w4w494.png

而在LinkedBlockingQueue中有2個鎖:

wKioL1b2TqzhaX9HAAANfu9FdwE655.png

wKiom1b2TjeAQpuMAAAL2dVdzdM519.png

其實到這裏,咱們已經能夠大體猜想出,LinkedBlockingQueue對於take/put使用了分別的鎖,從而比ArrayBlockingQueue在高併發下更具優點。


咱們再來看看其餘BlockingQueue:

DelayQueue:延遲隊列,其實是說,隊列中的元素生效的話,有個時間差。

PriorityQueue:優先級隊列,會提供Comparator來進行隊列中的元素的排序。

SynchronousQueue:這個隊列比較特殊,由於沒有存儲機制,實際上只是作了一個生產者和消費者的傳遞機制。


線程池介紹

若是任務到達時,纔開始建立線程,這實際上會讓任務的執行被延遲,因而產生了線程池的概念,若是在池子中已經存在了一批線程,那麼任務到達時天然省去了線程建立的時間,至關於提升了響應速度。其次,若是線程執行完任務後,在放入池子中,這至關於在複用線程,達到了資源節約的目的。固然,若是任務的執行時間是遠遠大於線程的建立/銷燬時間,其實就無所謂了。



快速建立線程池:Executors

wKiom1b2Vsyg8igYAAAxDv6DG20760.png


Executors提供了一系列的快速建立線程池的方法,好比:

wKioL1b2V-TBFjcAAAAdWmjC9AM355.png



wKiom1b2V2XwcB4ZAAAiRV0Onrs008.png


wKioL1b2WCDwT9pPAAAc9lhRhko375.png


建立數量固定的/單個的/緩存的  線程池。

能夠看到線程池的建立利用到了上面說起的BlockingQueue,隊列中的元素就是任務Runnable。

方法返回的都是ExecutorService的實現類:ThreadPoolExecutor。



線程池的核心:ThreadPoolExecutor

咱們直接來看看ThreadPoolExecutor的構造方法:

wKiom1b2WLOCpoqmAABhcZlS7Bk959.png

理解這些參數,對於理解線程池的原理有很大幫助:


corePoolSize:線程池的核心線程數量,是線程數目的一個穩定峯值。


maximumPoolSize:線程池的最大線程數量,若是corePoolSize依舊知足不了須要,那麼可讓線程增加至maximumPoolSize,一旦須要降低,那麼超出核心線程的那一部分線程資源將被回收。


workQueue:這個隊列是待處理的任務隊列。實際上,在ThreadPoolExecutor中除此以外還存在一個正在處理的工做隊列workers

wKioL1b2e2yDfnD9AAAI_i_YhEc158.png

keepAliveTime:超過核心線程數,又小於最大線程數目的線程在空閒的狀況下,多久回收。


threadFactory:線程工廠,實際上用的是默認的DefaultThreadFactory,經過代碼發現僅僅是針對Thread作了些設置(好比線程組/線程名稱/後臺/優先級等設置),將Runnable掛到Thread上而已。


handler:若是已經達到了最大線程數目,那麼對於任務只能開始拒絕了,這個就是拒絕處理的策略類。

相關文章
相關標籤/搜索