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.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Mastering Disruptor: High‑Performance In‑Memory Queue for Low‑Latency Java Applications

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencyDisruptorLow latencyIn-Memory Queue
Su San Talks Tech
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.