每個鎖建立多個條件佇列以避免虛假喚醒
多個條件佇列以實現更好的併發性。每個鎖使用單獨的條件佇列的優點。
- 它避免了虛假的喚醒和上下文切換。例如,如果您使用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週期。因此,如果您正在編寫併發庫實現,請記住您有更好的併發支援,並且您可以為每個鎖建立多個條件佇列以避免虛假喚醒。
相關文章
- 虛假喚醒
- 多執行緒——虛假喚醒執行緒
- 執行緒虛假喚醒問題剖析執行緒
- ReentrantLock的條件佇列ReentrantLock佇列
- java多執行緒之消費生產模型-使用synchronized解決虛假喚醒Java執行緒模型synchronized
- 多執行緒中使用Lock鎖定多個條件Condition的使用執行緒
- 使用slice和條件變數實現一個簡單的多生產者多消費者佇列變數佇列
- 併發條件佇列之Condition 精講佇列
- 6、JUC:傳統的生產者消費者問題,防止虛假喚醒問題
- jQuery DataTables新增自定義多個搜尋條件jQuery
- python 非同步佇列爬取多個網站Python非同步佇列網站
- 工作執行緒的喚醒及建立(19)執行緒
- 在命令列中使用 msbuild 定義多個編譯條件 DefineConstants 時出錯命令列UI編譯
- 一個高效能無鎖非阻塞連結串列佇列佇列
- excel將一個工作表根據條件拆分成多個工作簿Excel
- 條件佇列大法好:wait和notify的基本語義佇列AI
- Laravel 5.7 以佇列方法傳送郵件(三種場景)Laravel佇列
- 同一欄位多個查詢條件時遇到的一個問題
- Qt監聽Windows鎖屏、解鎖、休眠、喚醒、登入、登出訊息QTWindows
- 刷題系列 - 合併兩個順序佇列為一個新的佇列佇列
- 2020119-多執行緒暫停和喚醒執行緒
- 兩個棧實現佇列佇列
- 認識無鎖佇列佇列
- 假裝很忙的三個命令列工具命令列
- python基礎之如何用if語句判斷多個條件?Python
- Laravel 多條件查詢時粗心導致的一個 BUGLaravel
- 深入理解Java併發框架AQS系列(五):條件佇列(Condition)Java框架AQS佇列
- 條件變數如何避免丟失通知變數
- .NET 網路喚醒
- android 喚醒螢幕Android
- linux Shell 命令列-05-test 檢查某個條件是否成立Linux命令列
- 鮑勃大爺:將if/else中每個條件變為邏輯並列互拆而不依賴執行順序。
- Linq查詢之多個排序條件排序
- Linq兩個from查詢條件
- 用2個棧實現佇列佇列
- 兩個棧實現佇列操作佇列
- 如何寫一個任務佇列佇列
- Laravel 佇列傳送郵件Laravel佇列