Unlock Ultra‑Fast Java Concurrency with the Disruptor Framework
Disruptor is an open‑source, lock‑free Java framework that uses a ring‑buffer and sequencer to achieve ultra‑high‑throughput, handling millions of events per second, and is employed by projects like LMAX, Storm, Camel, and Log4j2, with detailed concepts, wait strategies, and usage examples.
Disruptor is an open‑source framework originally proposed by LMAX to solve queue lock problems under high concurrency, capable of processing six million orders per second in a single thread.
Official site: http://lmax-exchange.github.io/disruptor/
Why Disruptor Was Created
Current Java built‑in queue thread‑safety mechanisms:
ArrayBlockingQueue – array‑based queue with locks.
LinkedBlockingQueue – linked‑list queue with locks.
ConcurrentLinkedQueue – linked‑list queue using CAS.
Because locking often severely degrades performance, a lock‑free solution like Disruptor was developed.
Core Concepts
RingBuffer – the underlying data structure, a circular array that serves as the exchange point between threads.
Sequencer – manages sequence numbers for producers and consumers, coordinating synchronization.
Sequence – a numeric marker used to track the position of events in the RingBuffer and consumer progress.
SequenceBarrier – coordinates producer cursor and consumer sequences to prevent overwriting unprocessed events.
EventProcessor – listens to RingBuffer events and hands them to the actual consumer implementation.
EventHandler – the business‑logic consumer interface implemented by users.
Producer – the interface for threads that publish events into the RingBuffer.
Wait Strategy – determines how a consumer waits for producers to publish events.
Wait Strategies
BlockingWaitStrategy
The default strategy uses locks and conditions; it has the lowest CPU usage but is the least efficient.
SleepingWaitStrategy
Similar performance to BlockingWaitStrategy but minimizes impact on producer threads by using LockSupport.parkNanos(1) for waiting.
YieldingWaitStrategy
Suitable for low‑latency systems; it spins and calls Thread.yield() to allow other threads to run, recommended when the number of consumer threads is less than CPU cores.
BusySpinWaitStrategy
Provides the best performance for ultra‑low‑latency scenarios, again best when consumer threads are fewer than CPU cores.
PhasedBackoffWaitStrategy
Combines spinning, yielding, and a custom back‑off; used when CPU resources are scarce and throughput/latency are not critical.
Usage Example
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency> // Define the event type exchanged via Disruptor.
public class LongEvent {
private Long value;
public Long getValue() { return value; }
public void setValue(Long value) { this.value = value; }
} public class LongEventFactory implements EventFactory<LongEvent> {
public LongEvent newInstance() { return new LongEvent(); }
} public class LongEventHandler implements EventHandler<LongEvent> {
public void onEvent(LongEvent event, long sequence, boolean endOfBatch) throws Exception {
System.out.println("Consumer:" + event.getValue());
}
} public class LongEventProducer {
public final RingBuffer<LongEvent> ringBuffer;
public LongEventProducer(RingBuffer<LongEvent> ringBuffer) { this.ringBuffer = ringBuffer; }
public void onData(ByteBuffer byteBuffer) {
long sequence = ringBuffer.next();
Long data = null;
try {
LongEvent longEvent = ringBuffer.get(sequence);
data = byteBuffer.getLong(0);
longEvent.setValue(data);
try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
} finally {
System.out.println("Producer ready to send data");
ringBuffer.publish(sequence);
}
}
} public class DisruptorMain {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
EventFactory<LongEvent> eventFactory = new LongEventFactory();
int ringBufferSize = 1024 * 1024; // must be power of 2
Disruptor<LongEvent> disruptor = new Disruptor<>(eventFactory, ringBufferSize, executor,
ProducerType.SINGLE, new YieldingWaitStrategy());
disruptor.handleEventsWith(new LongEventHandler());
disruptor.start();
RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
LongEventProducer producer = new LongEventProducer(ringBuffer);
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
for (int i = 1; i <= 100; i++) {
byteBuffer.putLong(0, i);
producer.onData(byteBuffer);
}
disruptor.shutdown();
executor.shutdown();
}
}Core Design Principles
Ring‑array structure: Uses an array instead of a linked list to avoid garbage collection and to be cache‑friendly.
CPU caches consist of cache lines (typically 64 bytes). A Java long is 8 bytes, so a cache line holds eight longs. When a long array element is loaded, the neighboring seven are also cached, enabling very fast traversal.
Element positioning: Array length is a power of two (2ⁿ); bitwise operations accelerate index calculation, and the index is a long, preventing overflow even at massive QPS.
Lock‑free design: Producers and consumers claim a slot in the array and write/read directly, with atomic CAS ensuring thread safety.
Data Structure
The framework uses a RingBuffer as the queue data structure, a customizable circular array.
In addition to the array, a sequence number points to the next available element for producers and consumers.
Sequence
Disruptor numbers events with an incrementing sequence; processing always follows increasing order.
What is the advantage of the array + sequence design?
Like a HashMap, knowing the index gives O(1) access. The index can be computed as index = sequence % table.length or via bitwise operations when the length is a power of two.
Write Data Flow
Request to write m elements.
If m slots are available, the maximum sequence number is returned, ensuring no overwrite of unread elements.
Producer writes the elements.
Use Cases
Tests show Disruptor’s latency and throughput far surpass ArrayBlockingQueue, making it a candidate when the latter becomes a performance bottleneck.
Typical scenario: a producer‑consumer pattern with one producer and multiple consumers that must process events in order.
Example: reading sequentially from MySQL binlog and writing to Elasticsearch; the binlog requires a single producer, while Elasticsearch demands ordered writes, which Disruptor can handle without locking.
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.
