Java無(wú)鎖隊(duì)列 Disruptor 的原理深度解析
一、為什么需要 Disruptor?—— 背景與問(wèn)題
在高并發(fā)編程中,傳統(tǒng)的隊(duì)列(如 java.util.concurrent.ArrayBlockingQueue 或 LinkedBlockingQueue)在高性能場(chǎng)景下會(huì)成為瓶頸,主要問(wèn)題在于:
- 鎖競(jìng)爭(zhēng):生產(chǎn)者和消費(fèi)者之間使用同一把鎖(或讀寫(xiě)鎖),導(dǎo)致線程頻繁掛起、喚醒,上下文切換開(kāi)銷巨大。
- 偽共享:多個(gè)線程修改的、邏輯上獨(dú)立但物理上相鄰的變量,會(huì)因 CPU 緩存行的同步而導(dǎo)致性能急劇下降。
- 內(nèi)存分配開(kāi)銷:對(duì)于鏈表結(jié)構(gòu)的隊(duì)列,每次入隊(duì)出隊(duì)都可能涉及節(jié)點(diǎn)對(duì)象的創(chuàng)建和垃圾回收,在高吞吐下 GC 壓力大。
- 低效的遍歷:隊(duì)列的“頭出尾入”設(shè)計(jì),使得遍歷和批量操作不夠高效。
Disruptor 的目標(biāo)就是解決這些問(wèn)題,實(shí)現(xiàn)極低延遲、超高吞吐的線程間數(shù)據(jù)交換。
二、核心設(shè)計(jì)思想
Disruptor 不是一個(gè)傳統(tǒng)意義上的 FIFO 隊(duì)列,而是一個(gè) 基于數(shù)組的環(huán)形緩沖區(qū)(Ring Buffer) 。它的核心設(shè)計(jì)思想可以概括為以下幾點(diǎn):
1. 環(huán)形數(shù)組結(jié)構(gòu):

- 使用一個(gè)固定大小的數(shù)組預(yù)先分配所有內(nèi)存,避免運(yùn)行時(shí)動(dòng)態(tài)內(nèi)存分配。
- 數(shù)組元素(
Event)在初始化時(shí)就全部創(chuàng)建好,并被重復(fù)使用。這消除了 GC 壓力。 - 通過(guò)取模運(yùn)算(實(shí)際是高效的位運(yùn)算,要求數(shù)組大小為2的冪次)實(shí)現(xiàn)環(huán)形覆蓋,指針無(wú)限遞增,永不回收。
2. 無(wú)鎖設(shè)計(jì):
- 核心操作(生產(chǎn)與消費(fèi))完全無(wú)鎖(Lock-Free),通過(guò)內(nèi)存屏障(Memory Barrier) 和 CAS(Compare-And-Swap) 操作實(shí)現(xiàn)線程安全。
- 生產(chǎn)者之間通過(guò) CAS 競(jìng)爭(zhēng)下一個(gè)可寫(xiě)的槽位。
- 生產(chǎn)者和消費(fèi)者之間通過(guò)序列(Sequence) 的協(xié)調(diào)來(lái)工作,消費(fèi)者通過(guò)等待策略(Wait Strategy) 來(lái)感知新數(shù)據(jù)的到來(lái)。
3. 消除偽共享(Cache Line Padding):
- 識(shí)別出會(huì)被多個(gè)線程頻繁寫(xiě)入的關(guān)鍵變量(如生產(chǎn)者的
cursor,各個(gè)消費(fèi)者的Sequence)。 - 通過(guò)在這些變量前后添加無(wú)意義的填充字節(jié)(
padding),確保每個(gè)核心變量獨(dú)占一個(gè)完整的 CPU 緩存行(通常為64字節(jié)),防止它們被意外地加載到同一個(gè)緩存行中,從而避免一個(gè)線程的寫(xiě)入使另一個(gè)線程的整個(gè)緩存行失效。
4. 批量與依賴關(guān)系:
- 支持批量處理事件,能極大提高吞吐量。
- 可以顯式地構(gòu)建消費(fèi)者之間的依賴關(guān)系圖(如
A->B->C或A,B 都完成 -> C),實(shí)現(xiàn)高效的工作流。
三、核心組件與原理
1. 環(huán)形緩沖區(qū)(Ring Buffer)
這是 Disruptor 的物理存儲(chǔ)核心。它是一個(gè)固定大小的 Object[] 數(shù)組。每個(gè)位置被稱為一個(gè)“槽”(slot)。
size:必須是2的冪次(如 1024)。這樣sequence % size可以通過(guò)sequence & (size - 1)位運(yùn)算高效完成。cursor:生產(chǎn)者發(fā)布事件的序列號(hào)。它代表最后成功發(fā)布的事件的位置。這是一個(gè)Sequence對(duì)象。- 緩沖區(qū)本身不維護(hù)“頭”和“尾”指針,頭和尾的概念由生產(chǎn)者和消費(fèi)者的
Sequence共同決定。
2. 序列(Sequence)
Disruptor 的靈魂。它是一個(gè)使用 padding 封裝的長(zhǎng)整型(long)值。
- 所有需要追蹤進(jìn)度的組件都有自己的
Sequence:Ring Buffer有cursor(一個(gè)Sequence)。- 每個(gè)
EventProcessor(消費(fèi)者)有自己的Sequence,表示自己已處理完成的位置。 - 每個(gè)
Producer(如果是多生產(chǎn)者)也有自己的Sequence。
Sequence的值單調(diào)遞增,代表對(duì)應(yīng)組件在環(huán)形緩沖區(qū)中的位置。- 通過(guò)比較不同
Sequence的值,就能知道生產(chǎn)和消費(fèi)的進(jìn)度關(guān)系。
3. 序列屏障(Sequence Barrier)
消費(fèi)者用來(lái)協(xié)調(diào)工作、控制進(jìn)度的核心工具。
- 它持有:
- 生產(chǎn)者(或上游消費(fèi)者)的
cursor引用。 - 所有它所依賴的消費(fèi)者的
Sequence引用(用于構(gòu)建依賴圖)。
- 生產(chǎn)者(或上游消費(fèi)者)的
- 當(dāng)一個(gè)消費(fèi)者想要消費(fèi)事件時(shí),它會(huì)詢問(wèn)它的
SequenceBarrier:“我可以安全消費(fèi)的下一個(gè)事件是什么?” SequenceBarrier的邏輯是:返回min(生產(chǎn)者cursor, 所有依賴的消費(fèi)者的Sequence)。這確保了消費(fèi)者不會(huì)超越其依賴者,從而實(shí)現(xiàn)了無(wú)鎖的有序消費(fèi)。
4. 等待策略(Wait Strategy)
定義了消費(fèi)者如何等待新事件的到來(lái)。這是影響延遲和 CPU 占用的關(guān)鍵。
BlockingWaitStrategy:使用鎖和條件變量。最節(jié)省CPU,但延遲最高。適用于異步日志等場(chǎng)景。SleepingWaitStrategy:先自旋,后Thread.yield(),最后使用LockSupport.parkNanos(1)。平衡延遲和CPU。YieldingWaitStrategy:先自旋100次,然后調(diào)用Thread.yield()。延遲低,但會(huì)占用較多CPU。適用于要求極高吞吐、線程數(shù)小于CPU核心數(shù)的場(chǎng)景。BusySpinWaitStrategy:純自旋。延遲最低,但瘋狂消耗CPU。必須在綁定核心、線程數(shù)少于物理核心數(shù)的場(chǎng)景下使用。
5. 事件處理器(EventProcessor)
消費(fèi)者的執(zhí)行體。通常指 BatchEventProcessor。
- 它是一個(gè)線程,其
run()方法內(nèi)部是一個(gè)循環(huán):- 通過(guò)
SequenceBarrier.waitFor(nextSequence)等待自己可用的最大nextSequence。 - 獲取到
availableSequence后,從自己的當(dāng)前sequence到availableSequence批量處理事件。 - 調(diào)用
EventHandler.onEvent()處理每個(gè)事件。 - 處理完畢后,更新自己的消費(fèi)者
Sequence值。
- 通過(guò)
6. 生產(chǎn)者(Producer)
負(fù)責(zé)向 Ring Buffer 發(fā)布事件。分為單生產(chǎn)者(Single Producer) 和多生產(chǎn)者(Multi Producer) 兩種模式。
- 發(fā)布過(guò)程(兩階段提交):
- 申請(qǐng)空間(Claim):
- 單生產(chǎn)者:直接
nextSequence = cursor + 1(無(wú)競(jìng)爭(zhēng),無(wú)需CAS)。 - 多生產(chǎn)者:通過(guò) CAS 操作競(jìng)爭(zhēng)遞增一個(gè)
nextSequence。
- 單生產(chǎn)者:直接
- 發(fā)布(Publish):
- 生產(chǎn)者將數(shù)據(jù)寫(xiě)入
nextSequence對(duì)應(yīng)的slot。 - 寫(xiě)入完成后,必須調(diào)用
RingBuffer.publish(sequence)。 publish方法會(huì)先添加內(nèi)存屏障(store-store barrier,確保數(shù)據(jù)寫(xiě)入先于cursor更新),然后將cursor更新到sequence。cursor的更新會(huì)通知所有在SequenceBarrier上等待的消費(fèi)者。
- 生產(chǎn)者將數(shù)據(jù)寫(xiě)入
四、工作流程示例(單生產(chǎn)者 -> 單消費(fèi)者)
- 初始化:
- Ring Buffer 大小為 8,
cursor = -1。 - 消費(fèi)者
Sequence = -1。
- Ring Buffer 大小為 8,
- 生產(chǎn)者發(fā)布事件:
- 生產(chǎn)者需要發(fā)布事件
A。它申請(qǐng)下一個(gè)位置:next = cursor + 1 = 0。 - 它將事件
A的數(shù)據(jù)寫(xiě)入RingBuffer[0 & 7],即RingBuffer[0]。 - 寫(xiě)入完成后,調(diào)用
publish(0),更新cursor = 0。
- 生產(chǎn)者需要發(fā)布事件
- 消費(fèi)者消費(fèi)事件:
- 消費(fèi)者線程(
BatchEventProcessor)在循環(huán)中調(diào)用SequenceBarrier.waitFor(0)。 SequenceBarrier發(fā)現(xiàn)cursor (0) >= 0,且沒(méi)有依賴者,于是返回availableSequence = 0。- 消費(fèi)者知道自己當(dāng)前的
sequence (-1) < availableSequence (0),于是處理RingBuffer[0]的事件A。 - 處理完成后,將自己的
Sequence更新為0。
- 消費(fèi)者線程(
- 循環(huán)繼續(xù):生產(chǎn)者發(fā)布事件
B到slot 1,更新cursor=1。消費(fèi)者等待并處理,如此往復(fù)。
五、多消費(fèi)者與依賴關(guān)系
這是 Disruptor 最強(qiáng)大的部分。例如,我們有三個(gè)消費(fèi)者:C1(數(shù)據(jù)持久化),C2(數(shù)據(jù)統(tǒng)計(jì)),C3(發(fā)送消息,必須在 C1 和 C2 都完成后進(jìn)行)。
- 構(gòu)建依賴圖:
RingBuffer -> C1
-> C2
-> C3 (依賴 C1 和 C2)C3的SequenceBarrier會(huì)持有RingBuffer.cursor、C1.sequence和C2.sequence。- 當(dāng)
C3調(diào)用waitFor時(shí),SequenceBarrier返回的是min(生產(chǎn)者cursor, C1.sequence, C2.sequence)。 - 這意味著,即使生產(chǎn)者已經(jīng)發(fā)布了事件
10,但只要C1才處理到5,C3最多也只能拿到5。這樣就保證了C3不會(huì)跑到C1前面去,完全無(wú)鎖地實(shí)現(xiàn)了依賴。
六、總結(jié):Disruptor 高性能的秘訣
- 預(yù)分配內(nèi)存,消除GC:環(huán)形數(shù)組 + 對(duì)象復(fù)用。
- 無(wú)鎖并發(fā):CAS + 內(nèi)存屏障,取代重量級(jí)鎖。
- 消除偽共享:對(duì)關(guān)鍵序列進(jìn)行緩存行填充。
- 批量處理:一次等待,處理多個(gè)事件,攤薄開(kāi)銷。
- 依賴關(guān)系感知:通過(guò)序列比較實(shí)現(xiàn)無(wú)鎖的消費(fèi)者協(xié)調(diào),避免了“線程間握手”的開(kāi)銷。
- 關(guān)注點(diǎn)分離:將并發(fā)控制(Sequence, Barrier)、等待邏輯(WaitStrategy)、業(yè)務(wù)處理(EventHandler)清晰地解耦。
Disruptor 本質(zhì)上是一種精心設(shè)計(jì)的內(nèi)存隊(duì)列,它將共享變量的數(shù)量降到最低(核心就是那幾個(gè) Sequence),并通過(guò)硬件友好的方式(緩存行填充、內(nèi)存屏障)來(lái)操作它們,從而在軟件層面最大限度地壓榨出現(xiàn)代 CPU 和內(nèi)存子系統(tǒng)的性能。它特別適用于金融交易、高頻計(jì)算、事件溯源等對(duì)延遲和吞吐有極端要求的領(lǐng)域。
到此這篇關(guān)于Java無(wú)鎖隊(duì)列 Disruptor 的原理解析的文章就介紹到這了,更多相關(guān)java 無(wú)鎖隊(duì)列 disruptor 原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用EasyExcel實(shí)現(xiàn)百萬(wàn)數(shù)據(jù)導(dǎo)出的最佳實(shí)踐指南
這篇文章主要為大家詳細(xì)介紹了Java如何使用EasyExcel實(shí)現(xiàn)百萬(wàn)數(shù)據(jù)導(dǎo)出功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解下2026-01-01
Spring Boot + Vue 前后端分離項(xiàng)目如何踢掉已登錄用戶
這篇文章主要介紹了Spring Boot + Vue 前后端分離項(xiàng)目如何踢掉已登錄用戶,需要的朋友可以參考下2020-05-05
springboot實(shí)現(xiàn)基于aop的切面日志
這篇文章主要為大家詳細(xì)介紹了springboot實(shí)現(xiàn)基于aop的切面日志,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09
Spring Cloud Hystrix實(shí)現(xiàn)服務(wù)容錯(cuò)的方法
Hystrix是SpringCloud中重要的熔斷保護(hù)組件,由Netflix開(kāi)源,主要提供延遲和容錯(cuò)管理,以保障分布式系統(tǒng)的高可用性和魯棒性,通過(guò)封裝依賴項(xiàng)實(shí)現(xiàn)服務(wù)間隔離,引入回退邏輯應(yīng)對(duì)依賴服務(wù)故障,有效防止系統(tǒng)崩潰和服務(wù)級(jí)聯(lián)故障2024-10-10
java程序員必須要學(xué)會(huì)的linux命令總結(jié)(推薦)
下面小編就為大家分享一篇java程序員必須要學(xué)會(huì)的linux命令總結(jié)(推薦)。具有很好的參考價(jià)值。希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
Spring Boot + MyBatisPlus快速實(shí)現(xiàn)單表CRUD的示例
本文主要介紹了Spring Boot + MyBatisPlus快速實(shí)現(xiàn)單表CRUD的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2026-06-06
Spring?Data?JPA實(shí)現(xiàn)查詢結(jié)果返回map或自定義的實(shí)體類
這篇文章主要介紹了Spring?Data?JPA實(shí)現(xiàn)查詢結(jié)果返回map或自定義的實(shí)體類,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12

