Java中的阻塞隊列-LinkedBlockingQueue(二)

原文地址:http://benjaminwhx.com/2018/05/11/%E3%80%90%E7%BB%86%E8%B0%88Java%E5%B9%B6%E5%8F%91%E3%80%91%E8%B0%88%E8%B0%88LinkedBlockingQueue/

在集合框架里,想必大家都用過ArrayList和LinkedList,也經常在面試中問到他們之間的區別。ArrayList和ArrayBlockingQueue一樣,內部基于數組來存放元素,而LinkedBlockingQueue則和LinkedList一樣,內部基于鏈表來存放元素。

LinkedBlockingQueue實現了BlockingQueue接口,這里放一張類的繼承關系圖(圖片來自之前的文章:說說隊列Queue)

LinkedBlockingQueue不同于ArrayBlockingQueue,它如果不指定容量,默認為Integer.MAX_VALUE,也就是無界隊列。所以為了避免隊列過大造成機器負載或者內存爆滿的情況出現,我們在使用的時候建議手動傳一個隊列的大小。

2、源碼分析

2.1 屬性

/*** 節點類,用于存儲數據*/
static class Node<E> {E item;Node<E> next;Node(E x) { item = x; }
}/** 阻塞隊列的大小,默認為Integer.MAX_VALUE */
private final int capacity;/** 當前阻塞隊列中的元素個數 */
private final AtomicInteger count = new AtomicInteger();/*** 阻塞隊列的頭結點*/
transient Node<E> head;/*** 阻塞隊列的尾節點*/
private transient Node<E> last;/** 獲取并移除元素時使用的鎖,如take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();/** notEmpty條件對象,當隊列沒有數據時用于掛起執行刪除的線程 */
private final Condition notEmpty = takeLock.newCondition();/** 添加元素時使用的鎖如 put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();/** notFull條件對象,當隊列數據已滿時用于掛起執行添加的線程 */
private final Condition notFull = putLock.newCondition();

從上面的屬性我們知道,每個添加到LinkedBlockingQueue隊列中的數據都將被封裝成Node節點,添加的鏈表隊列中,其中head和last分別指向隊列的頭結點和尾結點。與ArrayBlockingQueue不同的是,LinkedBlockingQueue內部分別使用了takeLock 和 putLock 對并發進行控制,也就是說,添加和刪除操作并不是互斥操作,可以同時進行,這樣也就可以大大提高吞吐量。

這里如果不指定隊列的容量大小,也就是使用默認的Integer.MAX_VALUE,如果存在添加速度大于刪除速度時候,有可能會內存溢出,這點在使用前希望慎重考慮。

另外,LinkedBlockingQueue對每一個lock鎖都提供了一個Condition用來掛起和喚醒其他線程。

構造函數

public LinkedBlockingQueue() {// 默認大小為Integer.MAX_VALUEthis(Integer.MAX_VALUE);
}public LinkedBlockingQueue(int capacity) {if (capacity <= 0) throw new IllegalArgumentException();this.capacity = capacity;last = head = new Node<E>(null);
}public LinkedBlockingQueue(Collection<? extends E> c) {this(Integer.MAX_VALUE);final ReentrantLock putLock = this.putLock;putLock.lock();try {int n = 0;for (E e : c) {if (e == null)throw new NullPointerException();if (n == capacity)throw new IllegalStateException("Queue full");enqueue(new Node<E>(e));++n;}count.set(n);} finally {putLock.unlock();}
}

默認的構造函數和最后一個構造函數創建的隊列大小都為Integer.MAX_VALUE,只有第二個構造函數用戶可以指定隊列的大小。第二個構造函數最后初始化了last和head節點,讓它們都指向了一個元素為null的節點。

方法

同樣,LinkedBlockingQueue也有著和ArrayBlockingQueue一樣的方法,我們先來看看入隊列的方法。

2.3.1、入隊方法

LinkedBlockingQueue提供了多種入隊操作的實現來滿足不同情況下的需求,入隊操作有如下幾種:

  • void put(E e);
  • boolean offer(E e);
  • boolean offer(E e, long timeout, TimeUnit unit)。

put(E e)

public void put(E e) throws InterruptedException {if (e == null) throw new NullPointerException();int c = -1;Node<E> node = new Node<E>(e);final ReentrantLock putLock = this.putLock;final AtomicInteger count = this.count;// 獲取鎖中斷
    putLock.lockInterruptibly();try {//判斷隊列是否已滿,如果已滿阻塞等待while (count.get() == capacity) {notFull.await();}// 把node放入隊列中
        enqueue(node);c = count.getAndIncrement();// 再次判斷隊列是否有可用空間,如果有喚醒下一個線程進行添加操作if (c + 1 < capacity)notFull.signal();} finally {putLock.unlock();}// 如果隊列中有一條數據,喚醒消費線程進行消費if (c == 0)signalNotEmpty();
}

小結put方法來看,它總共做了以下情況的考慮:

  • 隊列已滿,阻塞等待。
  • 隊列未滿,創建一個node節點放入隊列中,如果放完以后隊列還有剩余空間,繼續喚醒下一個添加線程進行添加。如果放之前隊列中沒有元素,放完以后要喚醒消費線程進行消費。

offer(E e)

public boolean offer(E e) {if (e == null) throw new NullPointerException();final AtomicInteger count = this.count;if (count.get() == capacity)return false;int c = -1;Node<E> node = new Node<E>(e);final ReentrantLock putLock = this.putLock;putLock.lock();try {// 隊列有可用空間,放入node節點,判斷放入元素后是否還有可用空間,// 如果有,喚醒下一個添加線程進行添加操作。if (count.get() < capacity) {enqueue(node);c = count.getAndIncrement();if (c + 1 < capacity)notFull.signal();}} finally {putLock.unlock();}if (c == 0)signalNotEmpty();return c >= 0;
}

可以看到offer僅僅對put方法改動了一點點,當隊列沒有可用元素的時候,不同于put方法的阻塞等待,offer方法直接方法false。

offer(E e, long timeout, TimeUnit unit)

public boolean offer(E e, long timeout, TimeUnit unit)throws InterruptedException {if (e == null) throw new NullPointerException();long nanos = unit.toNanos(timeout);int c = -1;final ReentrantLock putLock = this.putLock;final AtomicInteger count = this.count;putLock.lockInterruptibly();try {// 等待超時時間nanos,超時時間到了返回falsewhile (count.get() == capacity) {if (nanos <= 0)return false;nanos = notFull.awaitNanos(nanos);}enqueue(new Node<E>(e));c = count.getAndIncrement();if (c + 1 < capacity)notFull.signal();} finally {putLock.unlock();}if (c == 0)signalNotEmpty();return true;
}

該方法只是對offer方法進行了阻塞超時處理,使用了Condition的awaitNanos來進行超時等待,這里為什么要用while循環?因為awaitNanos方法是可中斷的,為了防止在等待過程中線程被中斷,這里使用while循環進行等待過程中中斷的處理,繼續等待剩下需等待的時間。

轉載于:https://www.cnblogs.com/liyongliang/p/10697876.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/394305.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/394305.shtml
英文地址,請注明出處:http://en.pswp.cn/news/394305.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

自動加密企業關鍵業務數據 賽門鐵克推出全新信息保護解決方案

最新推出的Symantec Information Centric Security解決方案&#xff0c;能夠幫助企業隨時隨地對數據進行自動加密、跟蹤和撤銷&#xff0c;提供卓越的可見性和管控力 近日&#xff0c;全球網絡安全領域的領導者賽門鐵克公司宣布推出一款全新的高級信息保護工具 Symantec Inform…

leetcode312. 戳氣球(動態規劃)

有 n 個氣球&#xff0c;編號為0 到 n-1&#xff0c;每個氣球上都標有一個數字&#xff0c;這些數字存在數組 nums 中。 現在要求你戳破所有的氣球。如果你戳破氣球 i &#xff0c;就可以獲得 nums[left] * nums[i] * nums[right] 個硬幣。 這里的 left 和 right 代表和 i 相鄰…

碳鋼腐蝕速率計算公式_鎂合金輪轂螺栓連接的電偶腐蝕行為

環境污染和能源短缺促使日益發達的汽車工業大力推進構件輕量化&#xff0c;鎂合金是最輕的結構材料之一&#xff0c;構件采用鎂合金制造可以在減重的同時不降低結構強度&#xff0c;受到汽車工業的青睞。輪轂作為汽車的主要組成部件&#xff0c;其輕量化是汽車節能減排的有效途…

第七周總結

2019第七周作業 本周作業頭 這個作業屬于那個課程C語言程序設計II這個作業要求在哪里https://edu.cnblogs.com/campus/zswxy/computer-scienceclass1-2018/homework/2939我在這個課程的目標是理解指針數組和地址之前的關系及應用這個作業在那個具體方面幫助我實現目標practice參…

python大綱圖_Python課程大綱

課程大綱被分成6個部分&#xff0c;每個部分又被分解為多個階段&#xff0c; 而每個階段包含了多個Try, Workshop, FactToFace, Apply. 這里只列出部分&#xff0c;和階段&#xff1a;CHAPTER 0 : 預科[可選]Linux使用&#xff0c;常用CMD&#xff0c;服務配置&#xff0c;IDE&…

如何使用Google Authenticator在ASP.NET Core中設置兩因素身份驗證

介紹 (Introduction) In this article, we are going to learn how to perform two-factor authentication in an ASP.NET Core application using the Google Authenticator app.在本文中&#xff0c;我們將學習如何使用Google Authenticator應用程序在ASP.NET Core應用程序中…

280. Wiggle Sort

最后更新 二刷 這個題做得真蠢。上來想的復雜了&#xff0c;想的是quick sort之類的&#xff0c;然后一個一個交換。 實際上直接交換就行。。沒啥特別的。 回頭看一刷也是同樣的思考過程 宿命論啊。。 Time: O(n) Space: O(1) public class Solution {public void wiggleSort(i…

避免人為災難:盤點數據中心里十大愚蠢行為

對于企業運營&#xff0c;數據中心從設計、部署等各個環節都有極其嚴格的規范&#xff0c;保證簡單的“題目”不出錯也需要企業IT管理人員的智慧&#xff0c;在數據中心任何一個小錯誤往往會帶來巨大災難。數據中心從設計、部署、測試、運行、運維等各個環節都不能有任何的疏忽…

python中node.tag的用法_python在ui自動化中的一些常見用法

http://cn.python-requests.org/zh_CN/latest 可以查看requests庫的說明&#xff0c;pprint(res.json(),width30)可以對請求的返回值按照json格式化形式進行打印。常見的content-type 有application/x-www-form-urlencoded、application/json、application/xml。自動化測試操作…

leetcode1039. 多邊形三角剖分的最低得分(動態規劃)

給定 N&#xff0c;想象一個凸 N 邊多邊形&#xff0c;其頂點按順時針順序依次標記為 A[0], A[i], …, A[N-1]。 假設您將多邊形剖分為 N-2 個三角形。對于每個三角形&#xff0c;該三角形的值是頂點標記的乘積&#xff0c;三角剖分的分數是進行三角剖分后所有 N-2 個三角形的…

TRIZ解決問題方法

個人覺的成功是有規律的&#xff0c;那些成功的人士&#xff0c;都有一套處理事情的秘籍。只要我們的思維方式把那些秘籍融會貫通&#xff0c;并快速執行&#xff0c;我們有一天也會成功的。 TRIZ解決問題的5點方法。 1.確定最終目標。 2.列出阻礙因素 3.消除阻礙因素 4.可以利…

windows調用python_windows 快捷調用Python語言

本文主要向大家介紹了windows 快捷調用Python語言&#xff0c;通過具體的內容向大家展示&#xff0c;希望對大家學習Python語言有所幫助。場景1&#xff1a;某云平臺的賬號/或密碼比較長&#xff0c;一旦瀏覽器緩存失效&#xff0c;就要去郵件/文件查找&#xff0c;費時費力場景…

《量化投資:以MATLAB為工具》連載(1)基礎篇-N分鐘學會MATLAB(上)

http://blog.sina.com.cn/s/blog_4cf8aad30102uylf.html 《量化投資&#xff1a;以MATLAB為工具》連載(1)基礎篇-N分鐘學會MATLAB&#xff08;上&#xff09; 《量化投資&#xff1a;以MATLAB為工具》簡介 《量化投資&#xff1a;以MATLAB為工具》是由電子工業出版社&#xff0…

android-開源項目_我如何擺脫對開源的恐懼,并開始了自己的項目-以及如何做到。...

android-開源項目by Linea Brink Andersen通過Linea Brink Andersen 我如何擺脫對開源的恐懼&#xff0c;并開始了自己的項目-以及如何做到。 (How I crushed my fear of open source and started my own project — and how you can, too.) A week ago, I started an Open So…

本題要求實現函數輸出n行數字金字塔。_練習5-3 數字金字塔 (15分)

本題要求實現函數輸出n行數字金字塔。函數接口定義&#xff1a;void pyramid( int n );其中n是用戶傳入的參數&#xff0c;為[1, 9]的正整數。要求函數按照如樣例所示的格式打印出n行數字金字塔。注意每個數字后面跟一個空格。裁判測試程序樣例&#xff1a;#include <stdio.…

leetcode167. 兩數之和 II - 輸入有序數組(二分查找)

給定一個已按照升序排列 的有序數組&#xff0c;找到兩個數使得它們相加之和等于目標數。 函數應該返回這兩個下標值 index1 和 index2&#xff0c;其中 index1 必須小于 index2。 說明: 返回的下標值&#xff08;index1 和 index2&#xff09;不是從零開始的。 你可以假設每…

thinkcmf 橫向排列數據_利用python進行數據分析之數據清洗規整

1.處理缺失值數據使用dropna()時&#xff0c;注意里面參數axis、how、thresh的用法使用fillna()時&#xff0c;注意里面參數value、method、inplace、limit的用法2.數據轉換去重data.drop_duplicates(keeplast)#注意keep的用法映射map&#xff08;&#xff09;針對的是一維數組…

v$asm_diskgroup中state的說明

1.使用oracle賬號連接數據庫&#xff0c;查看v$asm_diskgroup 2.使用grid賬號連接ASM實例&#xff0c;查看v$asm_diskgroup 3.官方v$asm_diskgroup關于state的解釋 https://docs.oracle.com/en/database/oracle/oracle-database/19/refrn/V-ASM_DISKGROUP.html#GUID-5CF77719-7…

AutoMapper的介紹與使用(二)

AutoMapper的匹配 1&#xff0c;智能匹配 AutoMapper能夠自動識別和匹配大部分對象屬性: 如果源類和目標類的屬性名稱相同&#xff0c;直接匹配&#xff0c;不區分大小寫目標類型的CustomerName可以匹配源類型的Customer.Name目標類型的Total可以匹配源類型的GetTotal()方法…

站長快訊 WordPress跨站攻擊漏洞修補

WordPress中發現一些漏洞&#xff0c;攻擊者利用該漏洞可以發起跨站腳本攻擊&#xff0c;繞過WordPress安全性限制&#xff0c;獲取較為敏感的修訂歷史記錄的信息&#xff0c;或者綁架站點以用于DDoS攻擊。 CVE ID CVE-2015-8834 CVE-2016-5832 CVE-2016-5834 CVE-2016-5835 C…