併發編程之阻塞隊列

来源:https://www.cnblogs.com/jihuifeng/archive/2020/07/19/13340535.html
-Advertisement-
Play Games

隊列實質就是一種存儲數據的結構 通常用鏈表或者數組實現 一般而言隊列具備FIFO先進先出的特性,當然也有雙端隊列(Deque)優先順序隊列 主要操作:入隊(EnQueue)與出隊(Dequeue) BlockingQueue 隊列實質就是一種存儲數據的結構 通常用鏈表或者數組實現 一般而言隊列具備FI ...


隊列實質就是一種存儲數據的結構 通常用鏈表或者數組實現 一般而言隊列具備FIFO先進先出的特性,當然也有雙端隊列(Deque)優先順序隊列 主要操作:入隊(EnQueue)與出隊(Dequeue)

BlockingQueue

隊列實質就是一種存儲數據的結構 通常用鏈表或者數組實現 一般而言隊列具備FIFO先進先出的特性,當然也有雙端隊列(Deque)優先順序隊列 主要操作:入隊(EnQueue)與出隊(Dequeue)

1、ArrayBlockingQueue 由數組支持的有界隊列

2、LinkedBlockingQueue 由鏈接節點支持的可選有界隊列

3、PriorityBlockingQueue 由優先順序堆支持的無界優先順序隊列

4、DelayQueue 由優先順序堆支持的、基於時間的調度隊列

BlockingQueue底層依賴ReenTrantLock 和Condition(條件隊列只能在獨占模式下使用,共用模式不能用)(AQS框架實現)

ArrayBlockingQueue

 BlockingQueue<Ball> blockingQueue = new ArrayBlockingQueue<Ball>(1);初始化源碼分析
 public ArrayBlockingQueue(int capacity) {
        this(capacity, false); -------------false預設創建時非公平鎖
    }
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];-------初始化數組
lock = new ReentrantLock(fair);------創建非公平鎖
notEmpty = lock.newCondition(); ---------------初始化了兩個條件隊列,對應兩個條件對列notEmpty ,notFull

notFull = lock.newCondition();
}
// 兩個條件隊列如下圖:


為什麼會有連兩個條件隊列,因為有put(生產者),和take(消費者) 兩個操作 put的時候要看(Notfull)是不是滿了,take(Notempty)是看是不是空了。
put生產者操作源碼解讀
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); //----先上來獲取獨占鎖,才能進入下麵的邏輯。
try {
while (count == items.length)
notFull.await(); // 判斷當前隊列是不是已經滿了,如果count == items.length,那麼這個下線程就要入條件隊列等待

enqueue(e);
} finally {
lock.unlock();
}
}
/**
* 加入條件隊列等待,條件隊列入口
*/
public final void await() throws InterruptedException {
//如果當前線程被中斷則直接拋出異常
if (Thread.interrupted())
throw new InterruptedException();
//把當前節點加入條件隊列
Node node = addConditionWaiter();
//釋放掉已經獲取的獨占鎖資源
int savedState = fullyRelease(node);
int interruptMode = 0;
//如果不在同步隊列中則不斷掛起
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
//這裡被喚醒可能是正常的signal操作也可能是中斷
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
/**
* 走到這裡說明節點已經條件滿足被加入到了同步隊列中或者中斷了
* 這個方法很熟悉吧?就跟獨占鎖調用同樣的獲取鎖方法,從這裡可以看出條件隊列只能用於獨占鎖
* 在處理中斷之前首先要做的是從同步隊列中成功獲取鎖資源
*/
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//走到這裡說明已經成功獲取到了獨占鎖,接下來就做些收尾工作
//刪除條件隊列中被取消的節點
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//根據不同模式處理中斷
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
/**
* 這裡的判斷邏輯是:
* 1.如果現在不是中斷的,即正常被signal喚醒則返回0
* 2.如果節點由中斷加入同步隊列則返回THROW_IE,由signal加入同步隊列則返回REINTERRUPT
*/
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}

// 這個方法是把條件節點轉運到CLH同步隊列中並把信號量置為初始值0,返迴轉運成功與否標誌。只有把條件節點轉運到同步隊列才又可能被喚醒。
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
 

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 使用示例: $atomic = new Swoole\Atomic(); $serv = new Swoole\Server('127.0.0.1', '9501'); $serv->set([ 'worker_num' => 1, 'log_file' => '/dev/null' ]); // ...
  • 面對可能出現的網路延遲,不可預估的請求流量等情況,設計一個分散式系統,我們通常圍繞系統高可用,數據一致性的目標去規劃和實現,想要完全實現這個目標,卻並非易事。由此,分散式系統領域誕生了一個基本定理,即 CAP 定理,用於指導分散式系統的設計,從系統高可用,數據一致性,網路容錯三個角度將分散式系統的特 ...
  • 在已知的可直接作用於for迴圈的數據類型有以下幾種 第一類 list tuple dict set str 第二類 generator 可直接作用於for迴圈的對象統稱為可迭代對象:Iterable 可利用 isinstance() 判斷一個對象是否是 Iterable 對象 from collec ...
  • 網上關於Spring迴圈依賴的博客太多了,有很多都分析的很深入,寫的很用心,甚至還畫了時序圖、流程圖幫助讀者理解,我看了後,感覺自己是懂了,但是閉上眼睛,總覺得還沒有完全理解,總覺得還有一兩個坎過不去,對我這種有點笨的人來說,真的好難。當時,我就在想,如果哪一天,我理解了Spring迴圈依賴,一定要 ...
  • 關於編程語言中的註釋,其重要性基本上已為大家所共識。 然而關於註釋的規範,這個話題就像我們之前聊過的縮進、終止符和命名方式一樣,眾口難調。 註釋符通常可分為兩種,即行註釋與塊註釋(inline/block),它們在不同的編程語言中的符號可謂讓人眼花繚亂。 比如行註釋符,它至少有以下的 17 種之多( ...
  • 第十四章 高級函數 14.1重載成員函數 ​ 函數可以進行重載,成員函數(成員方法)實質上也是一種函數,所以成員函數也可以進行重載。 程式清單14.1 Rectangle.cpp #include <iostream> class Rectangle { private: int width; in ...
  • 在寫這篇文章時,我是滿懷感激與賞識之情的。 來誇一個人,講一個道理,寫給大家,也是寫給自己。 來自讀者的反饋 先說說事情的經過。 新書出版之後,昨天第一次看到(抱歉看到的比較晚)讀者的反饋。所謂反饋就是在書中留了GitHub的地址,如果書中有錯誤的地方,讀者可以通過該鏈接提交Issues(問題),來 ...
  • 之前只是很模糊的知道其意思:在request scope中,每個request創建一個新的bean;在session scope中,同一session中的bean都是一樣的。但是不知道怎麼用代碼去驗證它,今天可找到驗證它的代碼了。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...