Disruptor是LMAX開發的一個高性能隊列,研發的初衷是解決內存隊列的延遲問題(在性能測試中發現居然與I/O操做處於一樣的數量級)。基於Disruptor開發的系統單線程能支撐每秒600萬訂單,2010年在QCon演講後,得到了業界關注。2011年,企業應用軟件專家Martin Fowler專門撰寫長文介紹。同年它還得到了Oracle官方的Duke大獎。html
目前,包括Apache Storm、Camel、Log4j2在內的不少知名項目都應用了Disruptor以獲取高性能。java
咱們先知道disruptor是幹什麼的,而後筆者帶大家源碼搞一波,再來看看在log4j2中的運用。git
能夠這樣總結,Disruptor是LMAX開源的、用於替代併發線程間數據交換的環形隊列的、基本無鎖的(只有部分等待策略存在)、高性能的線程間通信框架。
github
Disruptor惟一可能遇到Java鎖的時候,就是在消費者等待可用事件進行消費時。而Disruptor爲這個等待過程,編寫了包括使用鎖和不使用鎖的多種策略,可根據不一樣場景和需求進行選擇。數組
開源:https://github.com/LMAX-Exchange/disruptor緩存
一個環形隊列,意味着首尾相連,對列能夠循環使用,使用數組來保存。環形隊列在JVM生命週期中一般是永生的,GC的壓力更小。安全
咱們來解釋一下這個圖:當前有一個consumer,停留在位置12,這時producer假設在位置3,這時producer的下一步是如何處理的呢?producer會嘗試讀取4,發現下一個能夠獲取,因此能夠安全獲取,而且通知一個阻塞的consumer起來活動。如此一直到下一圈11都是安全的(這裏咱們假設生產者比較快),當producer嘗試訪問12時發現不能繼續,因而自旋等待;當consumer消費時,會調用barrier的waitFor方法,waitFor看到前面最近的安全節點已經到了下一圈的11,因而consumer能夠無鎖的去消費當前12到下一圈11全部數據,能夠想象,這種方式比起synchronized要快上不少倍。數據結構
在高度競爭的狀況下,鎖的性能將超過原子變量的性能,可是更真實的競爭狀況下,原子變量的性能將超過鎖的性能。同時原子變量不會有死鎖等活躍性問題。能不用鎖,就不使用鎖,若是使用,也要將鎖的粒度最小化。併發
惟一使用鎖的就是消費者的等待策略實現類中,下圖。補充一句,生產者的等到策略就是LockSupport.parkNanos(1),再自旋判斷。
框架
名稱 | 措施 | 適用場景 |
---|---|---|
BlockingWaitStrategy | 加鎖 | CPU資源緊缺,吞吐量和延遲並不重要的場景 |
BusySpinWaitStrategy | 自旋 | 經過不斷重試,減小切換線程致使的系統調用,而下降延遲。推薦在線程綁定到固定的CPU的場景下使用 |
PhasedBackoffWaitStrategy | 自旋 + yield + 自定義策略 | CPU資源緊缺,吞吐量和延遲並不重要的場景 |
SleepingWaitStrategy | 自旋 + yield + sleep | 性能和CPU資源之間有很好的折中。延遲不均勻 |
TimeoutBlockingWaitStrategy | 加鎖,有超時限制 | CPU資源緊缺,吞吐量和延遲並不重要的場景 |
YieldingWaitStrategy | 自旋 + yield + 自旋 | 性能和CPU資源之間有很好的折中。延遲比較均勻 |
abstract class SingleProducerSequencerPad extends AbstractSequencer { protected long p1, p2, p3, p4, p5, p6, p7; SingleProducerSequencerPad(int bufferSize, WaitStrategy waitStrategy) { super(bufferSize, waitStrategy); } } public final class SingleProducerSequencer extends SingleProducerSequencerFields { protected long p1, p2, p3, p4, p5, p6, p7; //..省略 }
Java中經過填充緩存行,來解決僞共享問題的思路,如今可能已是老生常談,連Java8中都新增了sun.misc.Contended註解來避免僞共享問題。但在Disruptor剛出道那會兒,用緩存行來優化Java數據結構,這恐怕還很新潮。
1)經過sequence & (bufferSize - 1)
定位元素的index比普通的求餘取模(%)要快得多。sequence >>> indexShift
快速計算出sequence/bufferSize
的商flag(其實至關於當前sequence在環形跑道上跑了幾圈,在數據生產時要設置好flag。
2)合理使用Unsafe,CPU級別指令。實現更加高效地內存管理和原子訪問。
至於一些更細節的,下面源碼搞起來,仍是很簡單的。
源碼分析:Disruptor源碼解讀
參考: