Mastering Disruptor: High‑Performance In‑Memory Queue for Low‑Latency Java Applications
This article introduces the Disruptor in‑memory message queue, explains its architecture and core concepts such as Ring Buffer, Sequence, Sequencer, and Wait Strategies, and provides tuning guidance and a complete Java example for building low‑latency concurrent systems.
Introduction
Disruptor is a popular in‑memory message queue originating from LMAX’s research on concurrency, performance and non‑blocking algorithms.
Architecture Overview
Unlike typical distributed queues (e.g., RocketMQ) that consist of Producer, Broker, Consumer and a registry, Disruptor is a single‑process memory queue, so its architecture differs significantly.
LMAX’s use‑case diagram shows how Disruptor is applied.
Core Concepts
Ring Buffer
The Ring Buffer is the primary design element of Disruptor; since version 3.0 it only stores and updates data passing through Disruptor, and can be replaced by users in advanced scenarios.
Sequence
Disruptor uses Sequence objects to identify component positions. Each Consumer holds a Sequence similar to AtomicLong, but without false sharing.
False sharing occurs when multiple threads modify different variables that reside on the same CPU cache line, causing the line to be invalidated and reloaded, which degrades performance.
Sequencer
The Sequencer (single‑producer or multi‑producer) implements all concurrency algorithms to transfer data quickly and accurately between producers and consumers.
Sequence Barrier
A Sequence Barrier combines the Sequences generated by the Sequencer and those owned by Consumers to decide whether events are ready for processing.
Wait Strategy
Defines how Consumers wait for events.
Event Processor
Handles the event loop, owns the Consumer’s Sequence, and can invoke a user‑provided EventHandler after processing.
Event Handler
Implemented by the user to process events on the consumer side.
Disruptor Features
Multicast Events
Disruptor can deliver each event to all registered Consumers, unlike other queues where an event is consumed by a single Consumer.
Consumer Dependency Graph
Consumers can be coordinated via gating; a SequenceBarrier ensures that dependent Consumers finish before downstream Consumers start.
Pre‑allocated Memory
Disruptor aims for low latency by pre‑allocating event objects via an EventFactory, avoiding runtime allocation and ensuring thread‑safe updates.
Lock‑Free Concurrency
Most operations use lock‑free algorithms with CAS and memory barriers; only BlockingWaitStrategy uses a lock.
Tuning Options
Single vs. Multi‑Producer
Disruptor<LongEvent> disruptor = new Disruptor(
factory,
bufferSize,
DaemonThreadFactory.INSTANCE,
ProducerType.SINGLE,
new BlockingWaitStrategy()
);Choosing ProducerType.SINGLE or MULTI affects the Sequencer implementation. Benchmarks show higher throughput for single‑producer setups on typical hardware.
Wait Strategies
BlockingWaitStrategy – uses a lock and Condition.
SleepingWaitStrategy – uses LockSupport.parkNanos(1) without a Condition.
YieldingWaitStrategy – busy‑spins with Thread.yield() to reduce latency.
BusySpinWaitStrategy – highest performance, suitable only for dedicated low‑latency environments.
Official Example
Below is a minimal example that publishes long values to a Disruptor.
public class LongEvent {
private long value;
public void set(long value) { this.value = value; }
@Override public String toString() { return "LongEvent{value=" + value + '}'; }
} public class LongEventFactory implements EventFactory<LongEvent> {
@Override public LongEvent newInstance() { return new LongEvent(); }
} public class LongEventHandler implements EventHandler<LongEvent> {
@Override public void onEvent(LongEvent event, long sequence, boolean endOfBatch) {
System.out.println("Event: " + event);
}
} public class LongEventMain {
public static void main(String[] args) throws Exception {
int bufferSize = 1024;
Disruptor<LongEvent> disruptor = new Disruptor<>(LongEvent::new, bufferSize, DaemonThreadFactory.INSTANCE);
disruptor.handleEventsWith((event, sequence, endOfBatch) -> System.out.println("Event: " + event));
disruptor.start();
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
ByteBuffer bb = ByteBuffer.allocate(8);
for (long l = 0; ; l++) {
bb.putLong(0, l);
ringBuffer.publishEvent((event, seq, buffer) -> event.set(buffer.getLong(0)), bb);
Thread.sleep(1000);
}
}
}Conclusion
Disruptor’s design—memory pre‑allocation, lock‑free concurrency, and simple API—makes it a powerful choice for high‑performance in‑memory queuing.
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
