Boost Java Performance with Disruptor: A Hands‑On Guide to High‑Throughput Queues
This article introduces the high‑performance Java library Disruptor, explains its core concepts such as Ring Buffer, Sequence, and Wait Strategy, and provides a step‑by‑step demo with Maven configuration, code examples, and test results to help developers implement fast, lock‑free producer‑consumer queues.
1. Introduction to Disruptor
Disruptor is a high‑performance queue developed by the UK forex trading firm LMAX to solve latency problems of in‑memory queues, achieving throughput comparable to I/O operations.
Based on Disruptor, a single‑threaded system can sustain 6 million orders per second; after a 2010 QCon talk it attracted industry attention.
It is an open‑source Java framework designed for the producer‑consumer problem, aiming for maximum throughput (TPS) and minimal latency.
From a functional perspective, Disruptor implements a bounded queue, making it suitable for any producer‑consumer scenario. LMAX uses it to process up to 6 million TPS, and it can bring significant performance gains to other domains as well.
Disruptor is more a design pattern than a traditional framework, offering a way to dramatically improve performance for programs involving concurrency, buffering, producer‑consumer models, or transaction processing.
GitHub: https://github.com/LMAX-Exchange/disruptor
2. Core Concepts of Disruptor
Understanding the following domain objects helps grasp how Disruptor works.
Ring Buffer
As its name suggests, a circular buffer. Since version 3.0 its responsibility is reduced to storing and updating events; users can replace it with custom implementations for advanced scenarios.
Sequence
Sequences assign monotonically increasing numbers to events, tracking processing progress and preventing false sharing between CPU caches.
Note: preventing false sharing is one of the key reasons for Disruptor’s high performance.
Sequencer
The Sequencer interface has two implementations—SingleProducerSequencer and MultiProducerSequencer—that define the concurrent algorithm for passing data between producers and consumers.
Sequence Barrier
Maintains references to the main published sequence of the RingBuffer and to dependent consumer sequences, determining whether a consumer can process more events.
Wait Strategy
Defines how a consumer waits for the next event; Disruptor provides several strategies with different performance characteristics for various scenarios.
Event
In Disruptor terminology, an Event is the data exchanged between producer and consumer, defined by the user.
EventProcessor
Holds a specific consumer’s sequence and provides the event loop that invokes the event‑handling implementation.
EventHandler
User‑implemented interface that processes events; it is the actual consumer logic.
Producer
Any code that publishes events to the Disruptor; the framework does not define a specific interface for it.
3. Demo – Step‑by‑Step Implementation
Add Maven dependency
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>Message model
/**
* Message body
*/
@Data
public class MessageModel {
private String message;
}EventFactory implementation
public class HelloEventFactory implements EventFactory<MessageModel> {
@Override
public MessageModel newInstance() {
return new MessageModel();
}
}EventHandler (consumer)
@Slf4j
public class HelloEventHandler implements EventHandler<MessageModel> {
@Override
public void onEvent(MessageModel event, long sequence, boolean endOfBatch) {
try {
// Simulate async processing delay
Thread.sleep(1000);
log.info("Consumer starts processing");
if (event != null) {
log.info("Consumed message: {}", event);
}
} catch (Exception e) {
log.info("Consumer processing failed");
}
log.info("Consumer ends processing");
}
}BeanManager to obtain Spring beans
/**
* Retrieve instantiated beans
*/
@Component
public class BeanManager implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanManager.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() { return applicationContext; }
public static Object getBean(String name) { return applicationContext.getBean(name); }
public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); }
}MQManager configuration
@Configuration
public class MQManager {
@Bean("messageModel")
public RingBuffer<MessageModel> messageModelRingBuffer() {
ExecutorService executor = Executors.newFixedThreadPool(2);
HelloEventFactory factory = new HelloEventFactory();
int bufferSize = 1024 * 256; // must be power of two
Disruptor<MessageModel> disruptor = new Disruptor<>(factory, bufferSize, executor,
ProducerType.SINGLE, new BlockingWaitStrategy());
disruptor.handleEventsWith(new HelloEventHandler());
disruptor.start();
return disruptor.getRingBuffer();
}
}Service interface and implementation (producer)
public interface DisruptorMqService {
/**
* Send a message
*/
void sayHelloMq(String message);
}
@Slf4j
@Component
@Service
public class DisruptorMqServiceImpl implements DisruptorMqService {
@Autowired
private RingBuffer<MessageModel> messageModelRingBuffer;
@Override
public void sayHelloMq(String message) {
log.info("record the message: {}", message);
long sequence = messageModelRingBuffer.next();
try {
MessageModel event = messageModelRingBuffer.get(sequence);
event.setMessage(message);
log.info("Added message to queue: {}", event);
} catch (Exception e) {
log.error("failed to add event to ring buffer", e);
} finally {
messageModelRingBuffer.publish(sequence);
}
}
}Test class
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
public class DemoApplicationTests {
@Autowired
private DisruptorMqService disruptorMqService;
@Test
public void sayHelloMqTest() throws Exception {
disruptorMqService.sayHelloMq("Message arrived, Hello world!");
log.info("Message queue sent");
Thread.sleep(2000); // wait for async processing
}
}Test output
2023-04-05 14:31:18.543 INFO 7274 --- [main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl : record the message: 消息到了,Hello world!
2023-04-05 14:31:18.545 INFO 7274 --- [main] c.e.u.d.d.s.Impl.DisruptorMqServiceImpl : Added message to queue: MessageModel(message=消息到了,Hello world!)
2023-04-05 14:31:18.545 INFO 7274 --- [main] c.e.utils.demo.DemoApplicationTests : Message queue sent
2023-04-05 14:31:19.547 INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler : Consumer starts processing
2023-04-05 14:31:19.547 INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler : Consumed message: MessageModel(message=消息到了,Hello world!)
2023-04-05 14:31:19.547 INFO 7274 --- [pool-1-thread-1] c.e.u.d.disrupMq.mq.HelloEventHandler : Consumer ends processing4. Summary
The producer‑consumer pattern is common, and many message‑queue solutions can achieve similar results. Disruptor distinguishes itself by implementing the queue entirely in memory without locks, which is the primary reason for its exceptional efficiency.
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.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
