每個鎖建立多個條件佇列以避免虛假喚醒
多個條件佇列以實現更好的併發性。每個鎖使用單獨的條件佇列的優點。
- 它避免了虛假的喚醒和上下文切換。例如,如果您使用notifyAll進行傳統等待,則最終會喚醒正在等待不同條件的執行緒。
- 當您在單獨的條件佇列上等待時,您可以使用signal 而不是signalAll來進一步提高效能。
以下是在無界佇列之上的有界BlockingQueue的兩個經典實現。
每個鎖具有單獨的等待集
public class BlockingQueue<T> { private final Queue<T> queue; private final Lock lockObj = new ReentrantLock(); private final Condition empty = lockObj.newCondition(); private final Condition full = lockObj.newCondition(); private int maxLength; private int currentSize = 0; public BlockingQueue(int maxLength) { this.queue = new ArrayDeque<T>(); this.maxLength = maxLength; } public void offer(T elem) throws InterruptedException { lockObj.lock(); try { while (currentSize == maxLength) { full.await(); } queue.offer(elem); currentSize++; empty.signal(); } finally { lockObj.unlock(); } } public T poll() throws InterruptedException { lockObj.lock(); try { while (currentSize == 0) { empty.await(); } T elem = queue.poll(); currentSize--; full.signal(); return elem; } finally { lockObj.unlock(); } } } |
使用JMH測試吞吐量:
Benchmark Mode Cnt Score Error Units BenchmarkBlockingDequeu.testProduceAndConsume thrpt 25 12500542.933 ± 374127.076 ops/s |
舊的方式(單鎖和等待)
public class BlockingQueueWait<T> { private final Queue<T> queue; private final Object lockObj = new Object(); private int maxLength; private int currentSize = 0; public BlockingQueueWait(int maxLength) { this.queue = new ArrayDeque<T>(); this.maxLength = maxLength; } public void offer(T elem) throws InterruptedException { synchronized (lockObj) { while (currentSize == maxLength) { lockObj.wait(); } queue.offer(elem); currentSize++; lockObj.notifyAll(); } } public T poll() throws InterruptedException { synchronized (lockObj) { while (currentSize == 0) { lockObj.wait(); } T elem = queue.poll(); currentSize--; lockObj.notifyAll(); return elem; } } } |
使用JMH測試吞吐量:
Benchmark Mode Cnt Score Error Units BenchMarkBlockingWait.testProduceAndConsume thrpt 25 2702842.067 ± 24534.073 ops/s |
如果你仔細看看上面的實現,ops /s的差異是巨大的,其中大部分是由虛假的喚醒引起的,並且沒有使用顯式條件佇列和每個鎖的等待集,你最終會浪費寶貴的cpu週期。因此,如果您正在編寫併發庫實現,請記住您有更好的併發支援,並且您可以為每個鎖建立多個條件佇列以避免虛假喚醒。
相關文章
- 虛假喚醒
- 多執行緒——虛假喚醒執行緒
- 條件變數的虛假喚醒(spurious wakeups)問題變數
- 執行緒虛假喚醒問題剖析執行緒
- java多執行緒之消費生產模型-使用synchronized解決虛假喚醒Java執行緒模型synchronized
- ReentrantLock的條件佇列ReentrantLock佇列
- Android喚醒、解鎖螢幕Android
- 使用slice和條件變數實現一個簡單的多生產者多消費者佇列變數佇列
- 多執行緒中使用Lock鎖定多個條件Condition的使用執行緒
- 深入淺出AQS之條件佇列AQS佇列
- 工作執行緒的喚醒及建立(19)執行緒
- Excel不同列多條件計數Excel
- 併發條件佇列之Condition 精講佇列
- jQuery DataTables新增自定義多個搜尋條件jQuery
- 無鎖佇列佇列
- python 非同步佇列爬取多個網站Python非同步佇列網站
- Qt監聽Windows鎖屏、解鎖、休眠、喚醒、登入、登出訊息QTWindows
- 一個高效能無鎖非阻塞連結串列佇列佇列
- excel將一個工作表根據條件拆分成多個工作簿Excel
- Azure上每個VM多個IP地址
- 在命令列中使用 msbuild 定義多個編譯條件 DefineConstants 時出錯命令列UI編譯
- .NET 網路喚醒
- 【資料結構】迴圈佇列的front,rear指標以及佇列滿的條件、計算佇列長度資料結構佇列指標
- Laravel 5.7 以佇列方法傳送郵件(三種場景)Laravel佇列
- 10個隊中非空佇列按佇列號從小到大的順序串接成一條鏈佇列
- 假裝很忙的三個命令列工具命令列
- Laravel 一條 SQL 如何 count 多個欄位,Laravel 一條 sql 查詢每個分類的數量LaravelSQL
- POJ 2442-Sequence(優先佇列-m組n個數每組取一個求n個最小值)佇列
- 同一欄位多個查詢條件時遇到的一個問題
- 程式控制:程式的建立、終止、阻塞、喚醒和切換
- 刷題系列 - 合併兩個順序佇列為一個新的佇列佇列
- 小米Note3抬起喚醒人臉識別解鎖教程
- 條件佇列大法好:wait和notify的基本語義佇列AI
- 兩個棧實現佇列佇列
- android 喚醒螢幕Android
- 語音喚醒實現
- 認識無鎖佇列佇列
- 佇列、資源與鎖佇列