Java併發編程之阻塞隊列與Fork/Join框架

阻塞隊列

阻塞隊列是支持兩個附加操作的隊列。這兩個附加操作支持阻塞插入和移除方法:

  • 阻塞啓用的插入:當隊列滿時,它會阻塞插入元素的線程,直到隊列不滿意爲止。
  • 阻塞刪除:當隊列爲空時,檢索元素的線程等待隊列變爲非空。

阻塞隊列通常用於生產者-消費者場景。生產者是向隊列添加元素的線程,使用者是將元素從隊列中取出的線程,阻塞隊列是存儲和檢索元素的容器。

阻塞隊列的4種處理方式:

  1. 拋出異常:
    • add(e) 當隊列滿,再插入元素,拋出異常
    • remove() 當隊列空,再刪除元素,拋出異常
    • element() 獲取元素
  2. 返回特殊值:
    • offer(e) 插入元素時,插入成功返回true
    • poll() 移除元素,成功返回該值,否則返回null
    • peek()
  3. 一直阻塞:
    • put(e) 當阻塞隊列滿時,再插入時會阻塞生產者線程,直到隊列可用或中斷退出
    • take() 當隊列空,再移除元素會阻塞消費者線程,直到隊列不空
  4. 超時退出
    • 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是合併這些子任務並最終得到大任務結果的結果。

工作竊取算法

竊取工作是指從其他隊列中竊取要執行的任務的線程。通常使用兩端隊列。被盜任務線程總是從兩端隊列的頭部獲取要執行的任務,被盜任務線程總是從兩端隊列的尾部獲取要執行的任務。

其優點是充分利用線程進行並行計算,減少了線程之間的競爭。缺點是在某些情況下存在競爭,例如當隊列只有一個任務時,它將消耗更多的資源。

框架設計思路

首先,將任務劃分爲子任務,並將子任務連續劃分,直到子任務足夠小爲止。

然後,執行任務併合並結果。分離的子任務被放置在雙端隊列中,然後幾個啓動線程從雙端隊列獲得任務執行。執行結果放在隊列中,啓動一個線程從隊列中獲取數據,併合並這些線程。

示例

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章