阻塞隊列
阻塞隊列是支持兩個附加操作的隊列。這兩個附加操作支持阻塞插入和移除方法:
- 阻塞啓用的插入:當隊列滿時,它會阻塞插入元素的線程,直到隊列不滿意爲止。
- 阻塞刪除:當隊列爲空時,檢索元素的線程等待隊列變爲非空。
阻塞隊列通常用於生產者-消費者場景。生產者是向隊列添加元素的線程,使用者是將元素從隊列中取出的線程,阻塞隊列是存儲和檢索元素的容器。
阻塞隊列的4種處理方式:
- 拋出異常:
add(e)
當隊列滿,再插入元素,拋出異常remove()
當隊列空,再刪除元素,拋出異常element()
獲取元素
- 返回特殊值:
offer(e)
插入元素時,插入成功返回truepoll()
移除元素,成功返回該值,否則返回nullpeek()
- 一直阻塞:
put(e)
當阻塞隊列滿時,再插入時會阻塞生產者線程,直到隊列可用或中斷退出take()
當隊列空,再移除元素會阻塞消費者線程,直到隊列不空
- 超時退出
offer(e, time, unit)
隊列滿時再插入元素,阻塞,超時退出poll(time, unit)
隊列空時移除元素,阻塞,超時退出
Java中幾種阻塞隊列
- ArrayBlockingQueue: 數組結構構成的有界 FIFO 阻塞隊列
- LinkedBolckingQueue: 鏈表結構構成的有界 FIFO 阻塞隊列
- PriorityBlockingQueue: 支持優先級排序的無界阻塞隊列
- DelayQueue: 支持延時獲取元素,使用優先級隊列實現的無界阻塞隊列
- SynchronousQueue: 不存儲元素的阻塞隊列,不爲隊列元素維護存儲空間
- LinkedTransferQueue: 鏈表結構構成的無界阻塞隊列
- LinkedBlockingDeque: 鏈表構成的雙向阻塞隊列
ArrayBlockingQueue
數組阻塞隊列是一個使用數組實現的有界隊列,數組根據FIFO原則對元素進行排序。它還支持將生產者線程和使用者線程排隊的可選公平性策略,這不保證默認情況下對線程的公平訪問,並且可以公平地構造。公平減少了吞吐量,但減少了可變性,並避免了“不平衡”。
LinkedBlockingQueue
這是一個使用鏈表實現的有界阻塞隊列。默認長度和最大長度爲整數。最大值。隊列還根據FIFO原則對元素進行排序,以確定線程執行的順序。
PriorityBlockingQueue
這是一個支持優先級的無邊界的zusu隊列。默認情況下,採用自然升序,也可以通過構造函數指定comparator對元素進行排序。但它不能保證相同優先元素的順序。
底層是採用二叉最大堆來實現優先級排序的。
DelayQueue
這是一個支持延遲獲取元素的無邊界阻塞隊列,其隊列使用優先級隊列PriorityQueue實現。隊列中的元素必須實現延遲的接口。創建元素時,可以指定從隊列中檢索元素所需的時間,並且只能在元素過期時指定。
主要用於緩存,例如清除緩衝區中超時的數據。它還用於調度定時任務。
創建元素時,首先初始化延遲的接口;然後實現getDelay(TimeUnit Unit)方法,返回的值是當前元素需要延遲多長時間;最後實現compareTo(delayed other)方法以指定元素的順序。
當使用者從隊列中檢索元素時,如果元素未達到延遲時間,則當前線程將被阻塞。此外,leader變量被設置爲表示等待獲取隊列頭元素的線程。如果前導不爲空,則表示已經準備好等待獲取隊列頭元素,並且使用wait()方法使當前線程等待信號。如果leader爲空,則將當前線程設置爲leader,並使用waitnanos()方法使當前線程等待接收到的信號或延遲時間。
SynchronousQueue
與其他阻塞隊列不同,這是一個不存儲元素的阻塞隊列。每個Put操作必須等待Take操作,否則不能繼續添加元素,反之亦然。它分爲公平訪問隊列和不公平訪問隊列,默認情況下由不公平策略訪問。
隊列本身不存儲任何元素,並且適用於可傳遞的場景。它將生產者線程處理的數據直接傳輸到使用者線程。它的吞吐量高於鏈接阻塞隊列和數組阻塞隊列。
LinkedTransferQueue
這是由鏈表結構組成的FIFO的無邊界阻塞傳輸隊列。它採用先發制人的方式,即有先發制人的方式時,直接採取先發制人的方式,並佔據該位置,直到得到該位置,超時或中斷爲止。與其他阻塞隊列相比,有更多的TryTransfer方法和傳輸方法。
transfer(e,[timeout,unit])
方法: 如果當前有消費者正等待接收元素,該方法可以把生產者傳入的元素立刻傳輸給消費者。如果沒有消費者等待,該方法將元素存放在隊列的 tail 節點,等到該元素被消費者消費了才返回。tryTransfer(e,[timeout,unit])
方法: 試探生產者傳入的元素是否能直接傳給消費者。如果沒有消費者等待接收元素,返回false。該方法無論消費者是否接收都立即返回,而 transfer 方法必須等消費了才返回。
LinkedBlockingDeque
是一個由鏈表組成的雙向阻塞隊列。可以從隊列兩端插入和移除元素。
Fork/Join框架
該框架主要用於並行計算,將一個大的人分成幾個小的任務,最後對每個小任務的結果進行彙總,得到框架的結果。fork將一個大任務分成幾個子任務,並並行執行它們。join是合併這些子任務並最終得到大任務結果的結果。
工作竊取算法
竊取工作是指從其他隊列中竊取要執行的任務的線程。通常使用兩端隊列。被盜任務線程總是從兩端隊列的頭部獲取要執行的任務,被盜任務線程總是從兩端隊列的尾部獲取要執行的任務。
其優點是充分利用線程進行並行計算,減少了線程之間的競爭。缺點是在某些情況下存在競爭,例如當隊列只有一個任務時,它將消耗更多的資源。
框架設計思路
首先,將任務劃分爲子任務,並將子任務連續劃分,直到子任務足夠小爲止。
然後,執行任務併合並結果。分離的子任務被放置在雙端隊列中,然後幾個啓動線程從雙端隊列獲得任務執行。執行結果放在隊列中,啓動一個線程從隊列中獲取數據,併合並這些線程。
示例