JUC學習筆記-----LinkedBlockingQueueConcurrentLinkedQueueCopyOnWriteArrayList

LinkedBlockingQueue

基本的入隊出隊

初始化

public class LinkedBlockingQueue<E> extends AbstractQueue<E>implements BlockingQueue<E>, java.io.Serializable {// 靜態內部類 Node,用于存儲隊列元素及維護節點間關系static class Node<E> {// 存儲的實際元素,泛型 E 類型E item;/*** next 字段的三種可能情況說明:* - 真正的后繼節點:正常隊列鏈接場景,指向下一個節點* - 自己(this):出隊操作時的特殊標記,用于輔助處理并發下的節點狀態* - null:表示當前節點是隊列的最后一個節點,沒有后繼*/Node<E> next;// 構造方法,初始化節點時設置存儲的元素Node(E x) {item = x;}}}

初始化鏈表時,last?和?head?都指向新建的?Node<E>(null)?。該節點是?Dummy 節點(啞節點、哨兵節點)?,作用是占位,其?item?字段值為?null?,用于輔助?LinkedBlockingQueue?鏈表結構的初始化與操作(比如方便統一處理隊列空、頭尾指針等情況 )。

入隊

當一個節點入隊last = last.next = node;

再來一個節點入隊last = last.next = node;

出隊

// 出隊核心邏輯(基于 LinkedBlockingQueue 鏈表結構)
public E dequeue() {// 以下是關鍵出隊步驟,實際源碼會結合鎖、條件變量等保證線程安全,這里聚焦節點操作邏輯Node<E> h = head; // 1. 獲取真正存儲元素的首節點(head 是 dummy 節點,其 next 指向第一個有效節點)Node<E> first = h.next; // 2. 斷開原 head 節點的鏈接,并讓其 next 指向自身,輔助 GC 回收(避免內存泄漏)h.next = h; // 3. 更新 head 為真正的首節點(first),完成出隊后隊列頭指針遷移head = first; // 4. 取出首節點存儲的元素(隊列要彈出的元素)E x = first.item; // 5. 清空首節點的元素引用(幫助 GC,斷開元素引用鏈)first.item = null; // 6. 返回彈出的元素return x; 
}
  1. 保存頭節點:(h = head)
    用變量?h?暫存隊列當前的?head(dummy 節點 )。

  2. 獲取有效首節點:(first = h.next)
    通過?h.next?拿到真正存儲元素的第一個有效節點?first

  3. 斷開舊頭節點鏈接:(h.next = h
    讓?h.next = h(自己指向自己 ),切斷舊頭節點與隊列的關聯,輔助垃圾回收。

  4. 更新頭節點:(head = first)
    把?head?指向?first,完成隊列頭指針遷移,保證后續出隊邏輯正確。

  5. 取出元素
    從?first?節點中取出要彈出的元素?x

  6. 清空元素引用
    將?first.item?置為?null,幫助 GC 回收元素對象,避免內存泄漏。

  7. 返回結果
    返回彈出的元素?x,完成出隊操作。

加鎖分析

加鎖設計(高明之處)

  • 單鎖 vs 雙鎖
    • 單鎖:同一時刻最多允許一個線程(生產者 / 消費者二選一 )執行操作。
    • 雙鎖(putLock?、takeLock?):同一時刻允許一個生產者 + 一個消費者同時執行,消費者線程間、生產者線程間仍串行。

線程安全分析(按節點總數分類)

  1. 節點總數 > 2(含 dummy 節點)

    • putLock?保證?last?節點線程安全(入隊操作 )。
    • takeLock?保證?head?節點線程安全(出隊操作 )。
    • 兩把鎖隔離入隊、出隊操作,無競爭。
  2. 節點總數 = 2(1 個 dummy 節點 + 1 個正常節點)
    仍用兩把鎖鎖定不同對象(lasthead?相關 ),無競爭。

  3. 節點總數 = 1(僅 dummy 節點)
    take?線程因隊列為空,被?notEmpty?條件阻塞,有競爭時會阻塞等待。

// 用于 put(阻塞)、offer(非阻塞) 的鎖,控制入隊操作
private final ReentrantLock putLock = new ReentrantLock(); 
// 用于 take(阻塞)、poll(非阻塞) 的鎖,控制出隊操作
private final ReentrantLock takeLock = new ReentrantLock(); 

put操作

public void put(E e) throws InterruptedException {// 1. 校驗元素非空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; // 隊列元素數量(原子類,保證線程安全)// 2. 加鎖(可中斷鎖)putLock.lockInterruptibly();try {// 3. 隊列滿時等待(循環檢測,防止虛假喚醒)while (count.get() == capacity) {// 等待 notFull 條件(隊列非滿時被喚醒)notFull.await(); }// 4. 入隊操作enqueue(node); // 將新節點加入隊列尾部c = count.getAndIncrement(); // 元素數量 +1(先獲取舊值,再自增)// 5. 喚醒其他生產者(如果隊列仍有空位)if (c + 1 < capacity) {// 喚醒等待 notFull 的生產者線程notFull.signal(); }} finally {// 6. 釋放鎖putLock.unlock();}// 7. 喚醒消費者(如果隊列從空變為非空)if (c == 0) {// 喚醒等待 notEmpty 的消費者線程notEmpty.signal(); }
}// 輔助方法:入隊操作(私有方法,保證線程安全)
private void enqueue(Node<E> node) {// 隊列的 last 節點指向新節點,完成入隊last = last.next = node; 
}
  1. 校驗元素
    元素為?null?時拋出異常。

  2. 初始化與加鎖
    創建節點,獲取生產者鎖,可中斷加鎖。

  3. 隊列滿時等待
    隊列滿則循環等待?notFull?條件,釋放鎖并阻塞。

  4. 入隊與計數
    將節點加入隊列尾部,原子遞增元素數量。

  5. 喚醒生產者
    隊列仍有空位時,喚醒其他等待的生產者。(自己喚醒自己)

  6. 釋放鎖
    保證鎖釋放,避免死鎖。

  7. 喚醒消費者
    隊列入隊前為空時,喚醒等待的消費者。

take操作

public E take() throws InterruptedException {E x; // 存儲出隊的元素int c = -1; // 記錄出隊前的元素數量final AtomicInteger count = this.count; // 隊列元素數量(原子類,保證線程安全)final ReentrantLock takeLock = this.takeLock; // 消費者鎖// 1. 加鎖(可中斷鎖)takeLock.lockInterruptibly();try {// 2. 隊列空時等待(循環檢測,防止虛假喚醒)while (count.get() == 0) {// 等待 notEmpty 條件(隊列非空時被喚醒)notEmpty.await(); }// 3. 出隊操作x = dequeue(); // 從隊列頭部取出元素c = count.getAndDecrement(); // 元素數量 -1(先獲取舊值,再自減)// 4. 喚醒其他消費者(如果隊列仍有元素)if (c > 1) {// 喚醒等待 notEmpty 的消費者線程notEmpty.signal(); }} finally {// 5. 釋放鎖takeLock.unlock();}// 6. 喚醒生產者(如果隊列從滿變為非滿)if (c == capacity) {// 喚醒等待 notFull 的生產者線程signalNotFull(); }return x;
}
  1. 加鎖
    消費者線程加鎖,支持中斷。

  2. 隊列空時等待
    隊列空則循環等待?notEmpty?條件,釋放鎖并阻塞。

  3. 出隊與計數
    從隊列頭部取出元素,原子遞減元素數量。

  4. 喚醒消費者
    隊列仍有元素時,喚醒其他等待的消費者。

  5. 釋放鎖
    保證鎖釋放,避免死鎖。

  6. 喚醒生產者
    隊列出隊前滿時,喚醒等待的生產者。

  7. 返回結果
    返回出隊的元素。

LinkedBlockingQueue VS ArrayBlockingQueue

LinkedBlockingQueueArrayBlockingQueue
有界性支持支持有界(默認容量為?Integer.MAX_VALUE,可手動指定有界)強制有界(初始化必須指定容量,且不可動態擴容)
底層數據結構鏈表(Node?節點構成鏈表)數組(提前初始化固定大小的?Object?數組)
初始化特性懶惰初始化(首次入隊時創建節點,無需提前分配內存)提前初始化(創建時需指定容量,數組直接分配內存)
節點創建時機每次入隊(put/offer)時動態創建?Node初始化時提前創建數組元素(邏輯上的 “節點” 是數組槽位)
鎖機制雙鎖分離(putLock?控制入隊,takeLock?控制出隊)單鎖(ReentrantLock?同時控制入隊和出隊)
并發性能(典型場景)生產 / 消費可并行(雙鎖減少競爭),高并發下優勢明顯生產 / 消費互斥(單鎖競爭激烈),高并發下性能受限
內存開銷動態節點導致額外內存(Node?對象頭、指針等)數組連續存儲,內存更緊湊(無額外指針開銷)
擴容能力理論上可無限擴容(受限于?Integer.MAX_VALUE不可擴容(容量固定,滿了只能等待出隊)
適用場景高并發生產消費、需動態擴容、內存不敏感場景低并發、容量固定、內存敏感場景

ConcurrentLinkedQueue

CopyOnWriteArrayList

CopyOnWriteArraySet 是它的馬甲
底層實現采用了寫入時拷貝的思想,增刪改操作會將底層數組拷貝一份,更改操作在新數組上執行,這時不影響其它線程的并發讀,讀寫分離。
以新增為例:

public boolean add(E e) {synchronized (lock) {// 獲取舊的數組Object[] es = getArray();int len = es.length;// 拷貝新的數組(這里是比較耗時的操作,但不影響其它讀線程)es = Arrays.copyOf(es, len + 1);// 添加新元素es[len] = e;// 替換舊的數組setArray(es);return true;}
}

這里的源碼版本是 Java 11,在 Java 1.8 中使用的是可重入鎖而不是 synchronized

  • CopyOnWriteArraySet 等基于寫時拷貝(Copy - On - Write)思想的類,其他讀操作未加鎖,適合 “讀多寫少” 的應用場景,以?forEach?方法為例進行展示。
  • 適用場景:讀多寫少場景,讀操作無需加鎖,能高效并發讀取;寫操作因拷貝數組相對耗時,適合低頻寫操作的場景。
public void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);for (Object x : getArray()) {@SuppressWarnings("unchecked") E e = (E) x;action.accept(e);}
}

get弱一致性

時間點操作結果
1Thread-0?執行?getArray()拿到數組快照?array = [1, 2, 3]
2Thread-1?執行?getArray()拿到同一數組快照?array = [1, 2, 3]
3Thread-1?執行?remove(0)?→ 觸發寫時拷貝新建數組?arrayCopy = [2, 3],替換?array?為?arrayCopy
4Thread-0?執行?array[index]訪問的是舊數組?[1, 2, 3]?的元素

寫時拷貝的 “快照” 特性
讀線程(Thread-0)在時間點?1?拿到的是數組的快照(舊數組?[1, 2, 3])。即使后續寫線程(Thread-1)修改了數組(替換為?[2, 3]),讀線程仍會訪問自己持有的舊快照。
→?讀操作可能訪問到 “過期數據”,這就是弱一致性的體現。

迭代器弱一致性

CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
list.add(1);
list.add(2);
list.add(3);// 1. 獲取迭代器(此時迭代器持有數組快照:[1,2,3])
Iterator<Integer> iter = list.iterator(); // 2. 新線程修改集合(觸發寫時拷貝,數組變為 [2,3])
new Thread(() -> {list.remove(0); System.out.println(list); // 輸出: [2, 3]
}).start();// 3. 主線程繼續用迭代器遍歷
sleep1s(); // 等待子線程執行完畢
while (iter.hasNext()) {System.out.println(iter.next()); // 輸出: 1, 2, 3 
}
  1. 快照機制
    迭代器創建時,會拷貝當前數組的快照(如?[1,2,3]),后續集合的修改(如?remove)會生成新數組([2,3]),但迭代器仍持有舊快照。

  2. 無感知遍歷
    迭代器遍歷的是創建時的數組快照,不感知后續集合的修改 → 遍歷結果與 “當前集合實際數據” 不一致。

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

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

相關文章

小杰python高級(six day)——pandas庫

1.數據可視化用于繪制 DataFrame 數據圖形&#xff0c;它允許用戶直接從 DataFrame 創建各種類型的圖表&#xff0c;而不需要使用其他繪圖庫&#xff08;底層實際上使用了 Matplotlib&#xff09;。&#xff08;1&#xff09;plotDataFrame.plot(*args, **kwargs)功能&#xff…

第十六屆藍橋杯青少組C++省賽[2025.8.9]第二部分編程題(1 、慶典隊列)

參考程序&#xff1a;#include <iostream> using namespace std;int main() {int n, A;cin >> n >> A; // 輸入&#xff1a;n 和 A&#xff0c;用空格隔開cout << n / A; // 整數相除&#xff0c;自動向下取整return 0; }

C++進階:智能指針

目錄1. RAII與智能指針2. C庫中的智能指針2.1 智能指針auto_ptr2.2 智能指針unique_ptr2.3 智能指針shared_ptr3. shared_ptr的循環引用4. 智能指針的定值刪除器1. RAII與智能指針 上一篇文章學習了異常相關的知識&#xff0c;其中遺留了一個異常安全相關的問題。那就是異常的拋…

Tkinter 實現按鈕鼠標懸浮提示:兩種方案(繼承Frame與不繼承)

在 Tkinter 桌面應用開發中&#xff0c;為按鈕添加“鼠標懸浮提示”是提升用戶體驗的常用功能——無需點擊&#xff0c;只需將鼠標挪到按鈕上方&#xff0c;就能自動顯示按鈕功能說明。本文將詳細介紹兩種實現方案&#xff1a;不繼承 Frame 類&#xff08;快速簡潔版&#xff0…

20250814 最小生成樹總結

引子 啊啊額&#xff0c;從一張圖里抽出幾條邊&#xff0c;組成一棵樹&#xff0c;無環n?1n-1n?1條邊&#xff0c;就是生成樹。那么邊權和最小的生成樹就叫最小生成樹&#xff0c;最大生成樹同理。 kruskal最小生成樹 要求kruskal最小生成樹&#xff0c;我們首先用結構體數組…

數據大集網:實體店獲客引流的數字化引擎,解鎖精準拓客新密碼?

?在實體店面臨流量焦慮、獲客成本攀升的當下&#xff0c;實體店獲客引流工具的重要性愈發凸顯。如何在激烈的市場競爭中精準觸達目標客戶、構建可持續的客流增長模式&#xff1f;數據大集網憑借其創新的智能獲客體系與全鏈路服務能力&#xff0c;正成為萬千實體店突破增長瓶頸…

nginx --ssl證書生成mkcert

github https://github.com/FiloSottile/mkcert/releases網盤下載地址 https://pan.baidu.com/s/1XI0879pqu7HXZMnmQ9ztaw 提取碼: 1111windows使用示例

守拙以致遠:個人IP的長青之道|創客匠人

2025年被認為是AI應用全面爆發的一年。各種人工智能工具在寫作、制圖、剪輯等領域廣泛使用&#xff0c;大大提升了個人和團隊的工作效率。對于個人IP而言&#xff0c;這類工具的出現確實帶來了新的機會&#xff0c;但也伴隨著一種現象——一些人開始過度依賴甚至神化AI&#xf…

USB 3.0 LTSSM 狀態機

USB2.0在電源供應后&#xff0c;通過Pull Up D-來決定枚舉LS&#xff0c;Pull Up D有一個USB高速握手過程&#xff0c;來決定HS FS。USB3.0則會通過鏈路訓練&#xff08;Link Training&#xff09;&#xff0c;來準備USB3.0通信。每當我們插上USB線的時候&#xff0c;對于3.0的…

MySQL窗口函數與PyMySQL以及SQL注入

MySQL窗口函數與PyMySQL實戰指南&#xff1a;從基礎到安全編程 引言 在數據處理和分析領域&#xff0c;MySQL作為最流行的關系型數據庫之一&#xff0c;其窗口函數功能為數據分析提供了強大的支持。同時&#xff0c;Python作為數據分析的主要語言&#xff0c;通過PyMySQL庫與My…

高級項目——基于FPGA的串行FIR濾波器

給大家安利一個 AI 學習神站&#xff01;在這個 AI 卷成紅海的時代&#xff0c;甭管你是硬核開發者還是代碼小白&#xff0c;啃透 AI 技能樹都是剛需。這站牛逼之處在于&#xff1a;全程用 "變量名式" 幽默 生活化類比拆解 AI&#xff0c;從入門到入土&#xff08;啊…

JPrint免費的Web靜默打印控件:PDF打印中文亂碼異常解決方案

文章目錄JPrint是什么&#xff1f;中文亂碼&#xff08;Using fallback font xxx for xxxx&#xff09;1.字體嵌入2.客戶機字體安裝開源地址相關目錄導航使用文檔端口號修改代理使用場景打印服務切換中文亂碼解決方案 JPrint是什么&#xff1f; JPrint是一個免費開源的可視化靜…

MFT 在零售行業的實踐案例與場景:加速文件集成與業務協作的高效方案

零售行業競爭激烈、數字化轉型迭代迅速&#xff0c;業務對數據與檔案的傳輸、處理和整合要求極高。無論是新品上市市場數據&#xff0c;還是供應鏈物流單據&#xff0c;集成方式不論是通過API或是檔案傳輸, 對于傳輸的穩定性,安全性與性能, 都會直接影響決策效率與顧客體驗。MF…

OSG+Qt —— 筆記1 - Qt窗口加載模型(附源碼)

?? OSG/OsgEarth 相關技術、疑難雜癥文章合集(掌握后可自封大俠 ?_?)(記得收藏,持續更新中…) OSG+Qt所用版本皆為: Vs2017+Qt5.12.4+Osg3.6.5+OsgQt(master) 效果 代碼(需將cow.osg、reflect.rgb拷貝至工程目錄下) OsgForQt.ui main.cpp

開源安全云盤存儲:Hoodik 實現端到端數據加密,Docker快速搭建

以下是對 Hoodik 的簡單介紹&#xff1a; Hoodik 是一個使用 Rust 和 Vue 開發的輕量級自托管安全云存儲解決方案采用了非對稱RSA密鑰對和AES混合加密策略&#xff0c;從文件存儲加密到數據鏈路加密&#xff0c;全程保證數據安全支持Docker一鍵私有部署&#xff0c;數據和服務…

[C++] Git 使用教程(從入門到常用操作)

1. Git 簡介 Git 是一款分布式版本控制系統&#xff0c;用來跟蹤文件變化、協作開發、管理項目版本。 它是開源的&#xff0c;由 Linus Torvalds 在 2005 年開發&#xff0c;廣泛用于開源與企業項目中。 2. 安裝 Git Windows 前往 Git 官網 下載并安裝。 安裝時建議勾選 Git…

實盤回測一體的期貨策略開發:tqsdk獲取歷史數據并回測,附python代碼

原創內容第969篇&#xff0c;專注AGI&#xff0c;AI量化投資、個人成長與財富自由。 星球好多同學希望說說實盤&#xff0c;我們就從實盤開始吧。 我們選擇tqsdk給大家講解&#xff0c;tqsdk支持免費注冊&#xff0c;使用模擬賬戶&#xff0c;歷史和實時數據&#xff0c;方便…

大模型推理框架vLLM 中的Prompt緩存實現原理

背景&#xff1a;為什么需要Prompt緩存模塊&#xff1f;在大模型問答多輪對話應用場景中&#xff0c;不同請求的 Prompt 往往有相同的前綴&#xff0c;比如&#xff1a;第一次問答&#xff1a;你是一名專業的電子產品客服&#xff0c;負責回答客戶關于手機產品的咨詢。請根據以…

Python之Django使用技巧(附視頻教程)

概述 Django 是一個高級的 Python Web 框架&#xff0c;遵循 “batteries-included”&#xff08;內置電池&#xff09;理念&#xff0c;提供了構建 Web 應用所需的大部分組件&#xff0c;讓開發者可以專注于業務邏輯而不是底層細節。視頻教程&#xff1a;https://pan.quark.cn…

sqli-labs通關筆記-第44關 POST字符型堆疊注入(單引號閉合 手工注入+腳本注入3種方法)

目錄 一、堆疊注入 二、源碼分析 1、代碼審計 2、SQL注入安全性分析 三、堆疊手注法 1、進入靶場 2、正確用戶名密碼登錄 3、堆疊注入 4、查看數據庫 四、聯合手注法 1、獲取列數 2、確認回顯位 3、獲取數據庫名 4、獲取表名 5、獲取列名 6、獲取字段 7、總結…