Java中的ConcurrentLinkedQueue的使用小結(jié)
一、概述
ConcurrentLinkedQueue 是 Java 并發(fā)包(java.util.concurrent)中提供的無界非阻塞線程安全隊(duì)列,基于單向鏈表實(shí)現(xiàn),采用 CAS(Compare-and-Swap) 操作和 無鎖算法 保證并發(fā)安全。其核心設(shè)計(jì)目標(biāo)是高吞吐量和低延遲,適用于高并發(fā)場景下的生產(chǎn)者-消費(fèi)者模型。
關(guān)鍵特性
- 無界性:理論上容量無限,但受內(nèi)存限制。
- 非阻塞:操作永不阻塞線程,失敗立即返回(如
poll()在隊(duì)列為空時(shí)返回null)。 - 無鎖設(shè)計(jì):通過 CAS 和自旋機(jī)制實(shí)現(xiàn)線程安全,避免傳統(tǒng)鎖的開銷。
- FIFO 順序:嚴(yán)格遵循先進(jìn)先出原則。
- 弱一致性迭代器:遍歷時(shí)可能看到部分更新,但不會(huì)拋出
ConcurrentModificationException。
二、內(nèi)部數(shù)據(jù)結(jié)構(gòu)
1. 節(jié)點(diǎn)類Node<E>
private static class Node<E> {
volatile E item; // 存儲(chǔ)元素
volatile Node<E> next; // 指向下一個(gè)節(jié)點(diǎn)
// CAS 操作方法
boolean casItem(E cmp, E val) { ... }
boolean casNext(Node<E> cmp, Node<E> val) { ... }
}
- volatile 修飾:保證多線程可見性。
- CAS 操作:通過
Unsafe類實(shí)現(xiàn)原子性更新。
2. 隊(duì)列指針
- head:指向隊(duì)列頭部(可能滯后于實(shí)際頭節(jié)點(diǎn))。
- tail:指向隊(duì)列尾部(可能滯后于實(shí)際尾節(jié)點(diǎn))。
- 哨兵節(jié)點(diǎn):初始化時(shí)
head和tail均指向一個(gè)item=null的哨兵節(jié)點(diǎn)。
三、核心方法與實(shí)現(xiàn)原理
1.入隊(duì)操作(offer/add)
public boolean offer(E e) {
checkNotNull(e);
Node<E> newNode = new Node<>(e);
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) { // p 是尾節(jié)點(diǎn)
if (p.casNext(null, newNode)) {
if (p != t) casTail(t, newNode); // 更新 tail
return true;
}
} else if (p == q) { // 自引用節(jié)點(diǎn),重置 tail
p = (t != (t = tail)) ? t : head;
} else {
p = (p != t && t != (t = tail)) ? t : q;
}
}
}
- CAS 競爭:多個(gè)線程可能同時(shí)嘗試插入,僅一個(gè)成功。
- tail 滯后更新:僅在
p != t時(shí)更新tail,減少 CAS 操作頻率。
2.出隊(duì)操作(poll)
public E poll() {
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
if (item != null && p.casItem(item, null)) {
if (p != h) updateHead(h, p); // 更新 head
return item;
} else if ((q = p.next) == null) {
updateHead(h, p);
return null;
} else if (p == q) continue restartFromHead;
else p = q;
}
}
}
- CAS 移除:將頭節(jié)點(diǎn)的
item設(shè)為null,延遲物理刪除。 - head 更新:若
p != h,則更新head指針。
3.其他方法
- peek():獲取頭元素但不移除,邏輯與
poll()類似,不修改item。 - size():遍歷鏈表統(tǒng)計(jì)元素?cái)?shù),非線程安全(高并發(fā)下結(jié)果可能不準(zhǔn)確)。
- remove(Object o):遍歷鏈表移除首個(gè)匹配元素,返回是否成功。
四、無鎖并發(fā)控制機(jī)制
1. CAS 操作
- 原子性更新:通過
Unsafe.compareAndSwapObject實(shí)現(xiàn)對(duì)item和next的原子修改。 - 自旋重試:CAS 失敗時(shí)循環(huán)重試,而非阻塞線程。
2. 松弛不變量(Relaxed Invariants)
- head 滯后:可能指向已刪除節(jié)點(diǎn),僅在必要時(shí)更新(如遍歷時(shí)遇到自引用節(jié)點(diǎn))。
- tail 滯后:減少更新頻率,提升吞吐量。
3. 自引用節(jié)點(diǎn)
- 標(biāo)記刪除:出隊(duì)后,原頭節(jié)點(diǎn)的
next指向自身,防止其他線程誤用。 - 垃圾回收:物理刪除由 GC 處理,避免頻繁內(nèi)存操作。
五、適用場景
- 高并發(fā)生產(chǎn)者-消費(fèi)者模型:如日志處理、實(shí)時(shí)事件分發(fā)。
- 低延遲系統(tǒng):如高頻交易、游戲服務(wù)器。
- 無界緩沖需求:需動(dòng)態(tài)擴(kuò)展隊(duì)列長度的場景。
- 弱一致性要求:允許短暫的數(shù)據(jù)不一致(如遍歷時(shí))。
與LinkedBlockingQueue對(duì)比
| 特性 | ConcurrentLinkedQueue | LinkedBlockingQueue |
|---|---|---|
| 線程安全機(jī)制 | CAS 無鎖 | 鎖(ReentrantLock) |
| 容量限制 | 無界 | 可選有界/無界 |
| 阻塞操作 | 無 | 支持 put()/take() |
| 吞吐量 | 高 | 中等 |
| 內(nèi)存占用 | 節(jié)點(diǎn)結(jié)構(gòu)更輕量 | 可能更高 |
六、最佳實(shí)踐
- 避免頻繁調(diào)用 size():高并發(fā)下性能差,建議通過外部計(jì)數(shù)器統(tǒng)計(jì)。
- 合理預(yù)估容量:雖無界,但內(nèi)存耗盡可能引發(fā) OOM。
- 結(jié)合其他同步機(jī)制:如需精確控制,可與 Semaphore 或 CountDownLatch 聯(lián)用。
- 弱一致性遍歷:接受遍歷時(shí)可能遺漏新元素,適用于非強(qiáng)一致性場景。
七、源碼設(shè)計(jì)細(xì)節(jié)
- 哨兵節(jié)點(diǎn):初始化時(shí) head 和 tail 指向同一個(gè)哨兵節(jié)點(diǎn),簡化邊界條件處理。
- 自引用節(jié)點(diǎn):出隊(duì)后原頭節(jié)點(diǎn) next=self,標(biāo)記為待回收。
- CAS 優(yōu)化:節(jié)點(diǎn)構(gòu)造時(shí)使用 Unsafe.putObject 替代 volatile 寫操作,減少內(nèi)存屏障開銷。
八、總結(jié)
ConcurrentLinkedQueue 是 Java 并發(fā)編程中高性能無鎖隊(duì)列的典范,通過 CAS 和松弛不變量設(shè)計(jì),在保證線程安全的同時(shí)最大化吞吐量。適用于對(duì)延遲敏感、無需嚴(yán)格容量控制的場景,但需注意其無界特性可能帶來的內(nèi)存風(fēng)險(xiǎn)。理解其底層機(jī)制(如 CAS、自旋、自引用節(jié)點(diǎn))有助于在實(shí)際工程中合理應(yīng)用。
到此這篇關(guān)于Java中的ConcurrentLinkedQueue的使用小結(jié)的文章就介紹到這了,更多相關(guān)Java ConcurrentLinkedQueue內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?DelayQueue實(shí)現(xiàn)延時(shí)任務(wù)的示例詳解
DelayQueue是一個(gè)無界的BlockingQueue的實(shí)現(xiàn)類,用于放置實(shí)現(xiàn)了Delayed接口的對(duì)象,其中的對(duì)象只能在其到期時(shí)才能從隊(duì)列中取走。本文就來利用DelayQueue實(shí)現(xiàn)延時(shí)任務(wù),感興趣的可以了解一下2022-08-08
如何巧用HashMap一行代碼統(tǒng)計(jì)單詞出現(xiàn)次數(shù)詳解
這篇文章主要給大家介紹了關(guān)于如何巧用HashMap一行代碼統(tǒng)計(jì)單詞出現(xiàn)次數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
關(guān)于Java整合RocketMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)詳解
這篇文章主要介紹了關(guān)于Java整合RocketMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)詳解,RocketMQ作為一款純java、分布式、隊(duì)列模型的開源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時(shí)消息、消息回溯等,需要的朋友可以參考下2023-05-05
Java實(shí)現(xiàn)的可選擇及拖拽圖片的面板功能【基于swing組件】
這篇文章主要介紹了Java實(shí)現(xiàn)的可選擇及拖拽圖片的面板功能,涉及java基于swing組件選擇與操作圖片元素的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-01-01
Java Socket編程服務(wù)器響應(yīng)客戶端實(shí)例代碼
這篇文章主要介紹了Java Socket編程服務(wù)器響應(yīng)客戶端實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下2017-12-12
IDEA如何實(shí)現(xiàn)遠(yuǎn)程斷點(diǎn)調(diào)試jar包
這篇文章主要介紹了IDEA如何實(shí)現(xiàn)遠(yuǎn)程斷點(diǎn)調(diào)試jar包的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-06-06
使用maven整合Spring+SpringMVC+Mybatis框架詳細(xì)步驟(圖文)
這篇文章主要介紹了使用maven整合Spring+SpringMVC+Mybatis框架詳細(xì)步驟(圖文),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05
Java?webservice的POST和GET請(qǐng)求調(diào)用方式
這篇文章主要介紹了Java?webservice的POST和GET請(qǐng)求調(diào)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Java實(shí)現(xiàn)統(tǒng)計(jì)文件夾下所有文件的字?jǐn)?shù)
這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)統(tǒng)計(jì)文件夾下所有文件的字?jǐn)?shù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03

