善治病者,必醫其受病之處;
善救弊者,必塞其弊之源
書接上文線程池的設計思路,帶着如何設計拒絕策略的你,又回顧了整個流程,突然你想這事因排隊而起,能不能在排隊的隊列上有所改進。
第一部分 排隊也是值得考慮的
BlockingQueue:任務隊列
我們來看一下Doug Lea事先給我們設計好的隊列:
1. LinkedBlockingQueue
基於鏈表實現的(無界的)阻塞隊列,默認情況下容量是 Integer.MAX_VALUE,可以說是無界的。
只有調用這個構造函數纔可以設置容量capacity,否則都是Integer.MAX_VALUE。
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
2. ArrayBlockingQueue
基於數組實現的有界阻塞隊列。默認情況下,不保證FIFO的順序(By default, this ordering is not guaranteed).。因爲公平策略將會降低吞吐率,但是也降低了可變性以及避免了飢餓現象.(Fairness generally decreases throughput but reduces variability and avoids starvation.)
// 注意: 參數fair默認爲false, true -保證FIFO
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
3. SynchronousQueue
A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa(反之亦然).
同步隊列-不存儲任務的阻塞隊列。插入操作 和 刪除操作是一一對應的。就像一個空集合(acts as an empty collection)
三者之間的聯繫
- 吞吐率角度:
SynchronousQueue > LinkedBlockingQueue > ArrayBlockingQueue
- FIFO的角度:
LinkedBlockingQueue 直接按照隊列的FIFO設計;
SynchronousQueue, ArrayBlockingQueue 默認都是不保證FIFO順序的,可以在構造函數中打開fairness。
- 容量角度:
LinkedBlockingQueue默認容量是無限制的,直接打翻了“臨時工”的飯碗。即:maxiumPoolSize失去了意義。PS:2333, "正式工"已經哭暈倒廁所裏啦,往死裏幹。
世界本來就是不公平的,面對現實吧!!!
4. PriorityBlockingQueue
具有優先級的阻塞隊列,初始容量是11。Obviously, 它不保證FIFO,如果需要可以自定義類去打破其優先級(有病啊?OR閒得啦?).
還有幾種阻塞隊列,但是我認爲記住這四種差不多就夠用啦,後面的遇到再介紹。怕:消化不良 2333
BlockingQueue歸納一下:
盡情地發揮想象:
- LinkedBlockingQueue:爲了防止插隊,用繩索把每個人都串聯起來(PS:這繩索默認還是無限長地(Integer.MAX_VALUE)),MMP,還得請城管大隊長喝酒啊;
- ArrayBlockingQueue:綁起來貌似有點不厚道,還是把繩索解開,但是都別亂動啊。爲了不驚動城管,不準站太多人。
- PriorityBlockingQueue:城管大人站在隊列地尾部,對你投來迷之微笑,嚇得你毛骨悚然,怕遇到“同志”。正在發愣地時候,城管大人一隻手搭在你的肩膀上,老弟不請我吃一頓。此時你那還管什麼FIFO啊,直接拉着城管大人地手往店裏走。
- SynchronousQueue:城管大人說飽喝足,走出門口說:上面檢查,排隊影響市容市貌,不準排隊。你看着辦吧。只能(來一個走一個,走一個來一個)。PS:一首涼涼送給你
第二部分:學會拒絕
RejectedExecutionHandler 拒絕策略
1. AbortPolicy
簡單粗暴,你值得選擇。
默認策略:直接拒絕並拋出運行時異常:RejectedExecutionException。
2. DiscardPolicy
衝動是魔鬼,默默地拒絕。
直接拒絕但不拋異常。
3. DiscardOldestPolicy
爲了大局而犧牲局部利益
直接拒絕隊列的頭部,爲了不犯衆怒,你只好對隊列的第一個人說不。畢竟:得罪一個,取悅一羣啊。
4. CallerRunsPolicy
從哪來回哪去
交給調用者所在的線程來處理任務。
5. 自定義策略
隨心所欲
實現RejectedExecutionHandler接口。比如送個優惠券啥的…
RejectedExecutionExecutor總結
這裏假設:正是飢餓的時候,不讓吃飯誰能心裏舒服,誰也不願意啊,
- AboryPolicy :直接拒絕結果把人給打了一頓,這樣飯店也幹不下去啦…
- DiscardPolicy:陪着笑臉,打不還手罵不還口直的拒絕
- DiscardOldestPolicy: 爲了大部分人的利益而犧牲少數人。排隊的人在後面起鬨,質問前面的人怎麼還沒走啊。爲了平復後面的人,只能犧牲一下隊列首位啦。PS:電車悖論
- CallerRunsPolicy : 冷處理
- 自定義策略:爲了不得罪顧客,可能你要發個優惠券啥的。
通過這兩篇文章,我認爲大家對ThreadPoolExecutor應該有了一個清晰的認識。更爲詳細的內容,後續慢慢補充。接下來要實現你做大老闆的夢想——開連鎖店。