How Disruptor Achieves Lock-Free Speed: Single-Threaded Writes, Memory Barriers, and False Sharing
This article explains how the Disruptor library attains true lock‑free performance through single‑threaded writes, the use of memory barriers (volatile and happens‑before semantics), cache‑line alignment to eliminate false sharing, and a sequence‑barrier algorithm, providing code examples and practical optimization tips.
1. Single‑Threaded Write
Disruptor’s RingBuffer achieves a completely lock‑free design by enforcing a strict single‑threaded writer. This premise is essential; without it no lock‑free guarantee can be made. High‑performance frameworks such as Redis and Netty adopt the same principle.
2. Memory‑Barrier Optimization
Lock‑free correctness also requires memory barriers. In Java this is expressed with volatile fields and the happens‑before guarantee.
Linux kernel example uses smp_wmb() / smp_rmb() for write/read barriers (e.g., kfifo implementation). URL: https://github.com/opennetworklinux/linux-3.8.13/blob/master/kernel/kfifo.c
3. Cache‑Line Optimization – Eliminating False Sharing
Modern CPUs store data in cache lines (typically 64 bytes). When independent variables share a cache line, false sharing degrades performance.
Core concept: Sequence
The Sequence object behaves like an AtomicLong that marks progress and is padded so that its value occupies a full cache line (8 longs = 64 bytes), preventing false sharing.
4. Algorithmic Optimization – Sequence Barrier Mechanism
When a producer publishes an event it obtains a slot with: long sequence = ringBuffer.next(); In Disruptor 3.0 the SequenceBarrier works with Sequence to coordinate producer and consumer progress without locks or CAS.
Consumer sequence must be less than the producer sequence.
Consumer sequence must be less than any upstream consumer’s sequence.
Producer sequence cannot advance beyond the smallest consumer sequence.
This prevents overwriting events that have not yet been consumed.
SingleProducerSequencerPad#next Implementation (Java)
/** @see Sequencer#next(int) */
@Override
public long next(int n) {
if (n < 1) {
throw new IllegalArgumentException("n must be > 0");
}
// nextValue is a field of SingleProducerSequencer
long nextValue = this.nextValue;
long nextSequence = nextValue + n;
long wrapPoint = nextSequence - bufferSize; // check wrap‑around
long cachedGatingSequence = this.cachedValue;
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue) {
long minSequence;
while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue))) {
LockSupport.parkNanos(1L); // optional spin‑wait strategy
}
this.cachedValue = minSequence;
}
this.nextValue = nextSequence;
return nextSequence;
}Further discussion can be found at: https://zhuanlan.zhihu.com/p/21355046
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaEdge
First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
