LMAX Disruptor是一个高性能的跨线程消息传递库。它来自于LMAX对并发,性能和非阻塞算法的研究,今天形成了其Exchange基础架构的核心部分。
使用Disruptor
介绍
Michael Barker编辑了这个页面on Mar 2
2015 · 8修订
了解Disruptor是什么的最好方法是将其与理解的目的进行比较,并且非常相似。在Disruptor的情况下,这将是Java的。像排队一样,Disruptor的目的是在同一进程中的线程之间移动数据(例如消息或事件)。但是,Disruptor提供的一些关键功能可以将其与队列区分开来。他们是:
使用消费者依赖关系图向消费者发送多播事件。
为事件预分配内存。
可选择锁定。
核心概念
在我们可以了解Disruptor如何工作之前,值得定义将在整个文档和代码中使用的一些术语。对于具有DDD的人来说,将其视为Disruptor域的普遍存在的语言。
:环形缓冲区通常被认为是Disruptor的主要方面,但是从3.0起,Ring Buffer只负责存储和更新通过Disruptor的数据(Events)。而对于一些高级用例,可以完全由用户替代。
:Disruptor使用Sequences作为识别特定组件的位置的方法。每个消费者(EventProcessor)与Disruptor本身一样维护一个Sequence。大多数并发代码依赖于这些Sequence值的移动,因此Sequence支持AtomicLong的许多当前特征。事实上,2之间唯一的真正区别在于序列包含附加功能,以防止序列与其他值之间的虚假共享。
:音序器是Disruptor的真正核心。该接口的两个实现(单个生产者,多生产者)实现了所有并发算法,用于生产者和消费者之间快速,正确地传递数据。
:序列屏障由序列发生器产生,并包含对来自序列发生器的主要发布序列的引用以及任何依赖消费者的序列。它包含确定是否有任何可供消费者处理的事件的逻辑。
:等待策略确定消费者如何等待生产者将事件放入Disruptor。有关可选择无锁的部分提供更多详细信息。
事件:从生产者传递给消费者的数据单位。没有由用户完全定义的事件的特定代码表示。
:用于处理Disruptor事件的主事件循环,并具有消费者序列的所有权。有一个称为表示,它包含事件循环的有效实现,并将回调到EventHandler接口使用的提供的实现。
:由用户实现并代表Disruptor的用户的界面。
生产者:这是调用Disruptor排入事件的用户代码。这个概念在代码中也没有任何代表。
要将这些元素放在上下文中,下面是LMAX在其高性能核心服务(例如交换)中使用Disruptor的示例。
图1.具有一组依赖消费者的破坏者。
组播事件
这是队列和Disruptor之间最大的行为差异。当您有多个消费者在同一个Disruptor上侦听时,所有事件都将发布给所有消费者,而不必将单个事件发送给单个消费者的队列相反。Disruptor的行为旨在用于需要对相同数据进行独立多个并行操作的情况。来自LMAX的典型例子是我们有三个操作,将日志记录(将输入数据写入持久性日志文件),复制(将输入数据发送到另一台机器以确保存在数据的远程副本)和业务逻辑(真正的加工工作)。执行者风格的事件处理,。注意,它已经被锚定在现有的Disruptor类之上,并且没有被同样的一级支持处理,因此它可能不是达到这个特定目标的最有效的方式。
看看图1.可以看到有3个事件处理程序侦听(JournalConsumer,ReplicationConsumer和ApplicationConsumer)到Disruptor,这些事件处理程序中的每一个将接收Disruptor中可用的所有消息(按相同的顺序)。这允许每个消费者同时进行工作。
消费者依赖关系图
为了支持并行处理行为的真实应用,有必要支持消费者之间的协调。回顾上述示例,必须防止业务逻辑消费者在日志和复制消费者完成其任务之前进行进展。我们称这种概念门控,或者更正确地说,这种行为的超级特征称为门控。门控发生在两个地方。首先,我们需要确保生产者不会超过消费者。这可以通过调用RingBuffer.addGatingConsumers()将相关消费者添加到Disruptor来处理。其次,先前提到的情况是通过从必须首先完成其处理的组件构造包含序列的SequenceBarrier来实现的。
参考图1.有3个消费者从环形缓冲器中收听事件。在这个例子中有一个依赖图。ApplicationConsumer取决于JournalConsumer和ReplicationConsumer。这意味着JournalConsumer和ReplicationConsumer可以彼此并行地自由运行。通过从ApplicationConsumer的SequenceBarrier到JournalConsumer和ReplicationConsumer的Sequences的连接可以看出依赖关系。还要注意排序器与下游消费者的关系。其中一个角色是确保发布不包装环形缓冲区。为了做到这一点,没有一个下游消费者可能具有低于环形缓冲区序列的序列减去环形缓冲区的大小。然而,使用依赖图可以进行有趣的优化。因为ApplicationConsumers Sequence保证小于或等于JournalConsumer和ReplicationConsumer(即依赖关系确定),Sequencer只需要查看ApplicationConsumer的Sequence。在更一般的意义上,序列器只需要知道作为依赖关系树中的叶节点的消费者的序列。
事件预分配
Disruptor的目标之一是在低延迟环境中使用。在低延迟系统内,有必要减少或删除内存分配。在基于Java的系统中,目的是减少由于垃圾回收引起的数字停顿(在低延迟C / C ++系统中,由于存在于内存分配器上的争用,大量内存分配也是有问题的)。
为了支持这一点,用户能够预先分配Disruptor中事件所需的存储空间。在施工期间,EventFactory由用户提供,并将被分配给Disruptor的Ring Buffer中的每个条目。当向Disruptor发布新数据时,API将允许用户抓住构造的对象,以便他们可以调用该对象上的方法或更新字段。Disruptor提供保证,只要这些操作正确实施,这些操作将是并发的。
可选择无锁
迫切需要低延迟的另一个关键的实施细节是广泛使用无锁算法来实现Disruptor。所有的内存可见性和正确性保证都是使用内存障碍和/或比较和交换操作实现的。只有一个用例需要实际的锁定,并且在BlockingWaitStrategy中。这完全是为了使用条件,以便在等待新事件到达时可以停止消费线程。许多低延迟系统将使用忙等待来避免使用条件可能引起的抖动,但是在系统忙等待操作的数量可能会导致性能严重下降,特别是CPU资源严重受限时。例如虚拟化环境中的Web服务器。
入门
Michael Barker编辑了这个页面on Apr 15·27修订
获取Disruptor
Disruptor jar文件可从Maven Central获得,可以从那里集成到您的依赖管理器中。您还可以从Wiki的下载页面下载二进制文件。观察员会注意到,这些只是连接到Maven Central的二进制文件。
基本事件生产和消费
要开始使用Disruptor,我们将考虑一个非常简单而有创意的例子,即将生产者传递给消费者的单一长期价值,消费者将简单地打印出价值。首先我们将定义将携带数据的事件。
publicclassLongEvent{privatelongvalue;publicvoidset(longvalue){this.value=value;}}
为了让Disruptor为我们预先分配这些事件,我们需要一个将执行构造的EventFactoryimportcom.lmax.disruptor.EventFactory;publicclassLongEventFactoryimplementsEventFactory{publicLongEventnewInstance(){returnnewLongEvent();}}
一旦我们定义了事件,我们需要创建一个处理这些事件的消费者。在我们的例子中,我们要做的就是将控制台的值打印出来。importcom.lmax.disruptor.EventHandler;publicclassLongEventHandlerimplementsEventHandler{publicvoidonEvent(LongEventevent,longsequence,booleanendOfBatch){System.out.println("Event:"+event);}}
我们将需要这些事件的源,为了一个例子,我将假设数据来自某种I / O设备,例如网络或文件以ByteBuffer的形式。importcom.lmax.disruptor.RingBuffer;publicclassLongEventProducer{privatefinalRingBufferringBuffer;publicLongEventProducer(RingBufferringBuffer){this.ringBuffer=ringBuffer;}publicvoidonData(ByteBufferbb){longsequence=ringBuffer.next();//抓住下一个序列try{LongEvent event=ringBuffer.get(sequence);//获取Disruptor实体中的序列event.set(bb.getLong(0));//填充数据}finally{ringBuffer.publish(sequence);}}}事情发布变得比使用简单的队列更多地涉及到什么变得显而易见。这是因为事件预分配的愿望。它需要(在最低级别)消息发布的两阶段方法,即声明环形缓冲区中的时隙,然后发布可用数据。还需要在try / finally块中包装发布。如果我们在Ring Buffer中声明一个插槽(调用RingBuffer.next()),那么我们必须发布这个序列。否则可能会导致破坏者的状态的腐败。具体来说,在多生产者案例中,这将导致消费者停滞,无法重新启动而无法恢复。
使用版本3翻译器
使用版本3.0的Disruptor,添加了更丰富的Lambda风格的API,以帮助开发人员将此复杂性封装在环形缓冲区中,因此3.0之后,通过API的事件发布器/事件转换器部分发布消息的首选方法。例如
importcom.lmax.disruptor.RingBuffer;importcom.lmax.disruptor.EventTranslatorOneArg;publicclassLongEventProducerWithTranslator{privatefinalRingBufferringBuffer;publicLongEventProducerWithTranslator(RingBufferringBuffer){this.ringBuffer=ringBuffer;}privatestaticfinalEventTranslatorOneArgTRANSLATOR=newEventTranslatorOneArg(){publicvoidtranslateTo(LongEvent event,longsequence, ByteBuffer bb){event.set(bb.getLong(0));}};publicvoidonData(ByteBufferbb){ringBuffer.publishEvent(TRANSLATOR, bb);}}这种方法的另一个优点是,翻译器代码可以被拉入单独的类,并且可以轻松地单独测试单元。Disruptor提供了许多不同的接口(EventTranslator,EventTranslatorOneArg,EventTranslatorTwoArg等),可以实现来提供翻译器。原因是允许翻译器被表示为静态类或非捕获lambda(当Java 8滚动时)作为翻译方法的参数通过环缓冲区上的调用传递给翻译器。
最后一步是将整个事物联系起来。可以手动连接所有组件,但是它可能有点复杂,因此提供DSL以简化结构。一些更复杂的选项不能通过DSL获得,但是它适用于大多数情况。importcom.lmax.disruptor.dsl.Disruptor;importcom.lmax.disruptor.RingBuffer;importjava.nio.ByteBuffer;importjava.util.concurrent.Executor;importjava.util.concurrent.Executors;publicclassLongEventMain{publicstaticvoidmain(String[]args)throwsException{//用于为消费者构建新线程的执行程序Executor executor=Executors.newCachedThreadPool();//事件工厂LongEventFactory factory=newLongEventFactory();//指定环形缓冲器的大小,必须是2的幂intbufferSize=1024;//构建DisruptorDisruptordisruptor=newDisruptor<>(factory, bufferSize, executor);//连接处理程序disruptor.handleEventsWith(newLongEventHandler());//启动Disruptor,启动运行disruptor.start();//从Disruptor获取环形缓冲区用于发布RingBufferringBuffer=disruptor.getRingBuffer();LongEventProducer producer=newLongEventProducer(ringBuffer);ByteBuffer bb=ByteBuffer.allocate(8);for(longl=0;true; l++){bb.putLong(0, l);producer.onData(bb);Thread.sleep(1000);}}}
使用Java 8
Disruptor API的设计影响之一是Java 8将依靠功能接口的概念作为Java Lambdas的类型声明。Disruptor API中的大多数接口定义符合功能接口的要求,因此可以使用Lambda而不是自定义类,这样可以减少所需的锅炉位置。
importcom.lmax.disruptor.dsl.Disruptor;importcom.lmax.disruptor.RingBuffer;importjava.nio.ByteBuffer;importjava.util.concurrent.Executor;importjava.util.concurrent.Executors;publicclassLongEventMain{publicstaticvoidmain(String[]args)throwsException{//用于为消费者构建新的线程Executor executor=Executors.newCachedThreadPool();//指定环形缓冲器的大小,必须是2的幂intbufferSize=1024;//构建DisruptorDisruptordisruptor=newDisruptor<>(LongEvent::new, bufferSize, executor);//连接处理程序disruptor.handleEventsWith((event, sequence, endOfBatch)->System.out.println("Event:"+event));//启动Disruptor,启动运行disruptor.start();//从Disruptor获取环形缓冲区用于发布。RingBufferringBuffer=disruptor.getRingBuffer();ByteBuffer bb=ByteBuffer.allocate(8);for(longl=0;true; l++){bb.putLong(0, l);ringBuffer.publishEvent((event, sequence, buffer)->event.set(buffer.getLong(0)), bb);Thread.sleep(1000);}}}
注意不再需要一些类(例如处理程序,翻译器)。还要注意,publishEvent()仅用于lambda的引用是指传入的参数。如果我们要将该代码写成:ByteBufferbb=ByteBuffer.allocate(8);for(longl=0;true; l++){bb.putLong(0, l);ringBuffer.publishEvent((event, sequence)->event.set(bb.getLong(0)));Thread.sleep(1000);}
这将创建一个捕获lambda,这意味着它需要实例化一个对象来保存ByteBuffer bb变量,因为它将lambda通过到publishEvent()调用。这将产生额外的(不必要的)垃圾,因此如果需要低GC压力,则将优先传递参数到lambda的调用应该是首选的。
可以使用该方法引用而不是匿名的lamdbas,可以以这种方式重写示例。importcom.lmax.disruptor.dsl.Disruptor;importcom.lmax.disruptor.RingBuffer;importjava.nio.ByteBuffer;importjava.util.concurrent.Executor;importjava.util.concurrent.Executors;publicclassLongEventMain{publicstaticvoidhandleEvent(LongEventevent,longsequence,booleanendOfBatch){System.out.println(event);}publicstaticvoidtranslate(LongEventevent,longsequence, ByteBufferbuffer){event.set(buffer.getLong(0));}publicstaticvoidmain(String[]args)throwsException{//用于为消费者构建新的线程Executor executor=Executors.newCachedThreadPool();
//指定环形缓冲器的大小,必须是2的幂
intbufferSize=1024;//构建DisruptorDisruptordisruptor=newDisruptor<>(LongEvent::new, bufferSize, executor);//连接处理程序disruptor.handleEventsWith(LongEventMain::handleEvent);//启动Disruptor,启动运行disruptor.start();//从Disruptor获取环形缓冲区用于发布。RingBufferringBuffer=disruptor.getRingBuffer();ByteBuffer bb=ByteBuffer.allocate(8);for(longl=0;true; l++){bb.putLong(0, l);ringBuffer.publishEvent(LongEventMain::translate, bb);Thread.sleep(1000);}}}
基本调谐选项
使用上述方法将在最广泛的部署场景集中起作用。但是,如果您能够对Disruptor运行的硬件和软件环境做出某些假设,那么您可以利用多个调优选项来提高性能。调整,单个对多个生产者和替代等待策略有两个主要选项。
单一与多个生产者
提高并发系统性能的最佳方式之一就是单一作者主体,这适用于Disruptor。如果您处于这样的情况,那么只会有一个线程在Disruptor中生成事件,那么您可以利用此功能获得更多的性能。
publicclassLongEventMain{publicstaticvoidmain(String[]args)throwsException{//.....//构建Disruptor与SingleProducerSequencerDisruptordisruptor=newDisruptor(factory, bufferSize, ProducerType.SINGLE,newBlockingWaitStrategy(), executor);//.....}}
为了说明通过这种技术可以实现多少性能优势,我们可以在OneToOne性能测试中更改生产者类型。测试运行在i7 Sandy Bridge MacBook Air上。
多生产者
Run 0, Disruptor=26,553,372 ops/secRun 1, Disruptor=28,727,377 ops/secRun 2, Disruptor=29,806,259 ops/secRun 3, Disruptor=29,717,682 ops/secRun 4, Disruptor=28,818,443 ops/secRun 5, Disruptor=29,103,608 ops/secRun 6, Disruptor=29,239,766 ops/sec
单生产者
Run 0, Disruptor=89,365,504 ops/secRun 1, Disruptor=77,579,519 ops/secRun 2, Disruptor=78,678,206 ops/secRun 3, Disruptor=80,840,743 ops/secRun 4, Disruptor=81,037,277 ops/secRun 5, Disruptor=81,168,831 ops/secRun 6, Disruptor=81,699,346 ops/sec
替代等待策略
Disruptor使用的默认等待策略是BlockingWaitStrategy。BlockingWaitStrategy内部使用一个典型的锁和条件变量来处理线程唤醒。BlockingWaitStrategy是可用的等待策略中最慢的一个,但是对于CPU使用情况来说最保守,并且可以在最广泛的部署选项中提供最一致的行为。然而,再次了解部署的系统可以允许额外的性能。
像BlockingWaitStrategy一样,SleepingWaitStrategy尝试使用简单的忙等待循环来保守CPU使用率,但是LockSupport.parkNanos(1)在循环中使用一个调用。在典型的Linux系统上,这将暂停线程大约60μs。然而,它具有生产线程不需要采取任何其他增加适当的计数器并且不需要用于表示条件变量的代价的优点。然而,在生产者和消费者线程之间移动事件的平均延迟会更高。在不需要低延迟的情况下,它最适合使用,但是对生产线程的影响很小。常见的用例是异步日志记录。
YieldingWaitStrategy是可以在低延迟系统中使用的2种等待策略之一,其中可以选择刻录CPU周期,以提高延迟。YieldingWaitStrategy将忙于等待序列增加到适当的值。Thread.yield()将调用循环体内的其他排队线程来运行。这是建议的等待策略,当需要非常高的性能和事件处理程序线程的数量少于逻辑内核的总数时,例如,启用超线程。
BusySpinWaitStrategy是性能最高的等待策略,但对部署环境构成最大限制。只有当事件处理程序线程数小于框上物理内核的数量时,才应使用此等待策略。应该禁用超线程。
从环形缓冲区清除对象
当通过Disruptor传递数据时,对象可能比预期的寿命更长。为了避免这种情况发生,可能需要在处理之后清除事件。如果您有一个事件处理程序清除同一处理程序中的值就足够了。如果您有一连串的事件处理程序,那么您可能需要在链末端处放置一个特定的处理程序来处理清除对象。
classObjectEvent{T val;voidclear(){val=null;}}publicclassClearingEventHandlerimplementsEventHandler>{publicvoidonEvent(ObjectEventevent,longsequence,booleanendOfBatch){//这里没有调用clear将导致与事件相关联的//对象生存直到一旦环形缓冲区被包围开头就覆盖它。event.clear();}}publicstaticvoidmain(String[] args){Disruptor>disruptor=newDisruptor<>(()->ObjectEvent(), bufferSize, executor);disruptor.handleEventsWith(newProcessingEventHandler()).then(newClearingObjectHandler());}
开发Disruptor
编译
编码标准
设计与实施
单生产者算法
多生产者算法
已知的问题
在32位Linux系统上看起来LockSupport.parkNanos()相当昂贵,因此不推荐使用SleepingWaitStrategy。
Disruptor 2.x的单个发布者和单个事件处理器的示例
Disruptor 2.x的代码示例
下面的代码是单个生产者和单个消费者使用便利界面EventHandler实现消费者的示例。消费者运行在单独的线程上,当它们变得可用时接收条目。
RingBuffer事件作为数据交换项目。这些事件对象像应用程序一样简单或复杂。public final class ValueEvent
{
private long value;
public long getValue()
{
return value;
}
public void setValue(final long value)
{
this.value = value;
}
public final static EventFactory EVENT_FACTORY = new EventFactory()
{
public ValueEvent newInstance()
{
return new ValueEvent();
}
};
}
事件处理器为了方便实现这个接口。final EventHandler handler = new EventHandler()
{
public void onEvent(final ValueEvent event, final long sequence, final boolean endOfBatch) throws Exception
{
// process a new event.
}
};
设置RingBuffer和障碍。RingBuffer ringBuffer =
new RingBuffer(ValueEvent.EVENT_FACTORY,
new SingleThreadedClaimStrategy(RING_SIZE),
new SleepingWaitStrategy());
SequenceBarrier barrier = ringBuffer.newBarrier();
BatchEventProcessor eventProcessor = new BatchEventProcessor(barrier, handler);
ringBuffer.setGatingSequences(eventProcessor.getSequence());
// Each EventProcessor can run on a separate thread
EXECUTOR.submit(eventProcessor);
简化设置。使在大多数情况下,DSLWizard可用于用DSLWizard时的等效环形缓冲区和处理器设置将是:Disruptor disruptor =
new Disruptor(ValueEvent.EVENT_FACTORY, EXECUTOR,
new SingleThreadedClaimStrategy(RING_SIZE),
new SleepingWaitStrategy());
disruptor.handleEventsWith(handler);
RingBuffer ringBuffer = disruptor.start();
发布者按顺序发布事件以发布到EventProcessors。// Publishers claim events in sequence
long sequence = ringBuffer.next();
ValueEvent event = ringBuffer.get(sequence);
event.setValue(1234); // this could be more complex with multiple fields
// make the event available to EventProcessors
ringBuffer.publish(sequence);
或与Disruptor DSL:disruptor.publishEvent(eventTranslator);
其中eventTranslator是类com.lmax.disruptor.EventTranslator的一个实例。将调用translateTo方法将数据从环形缓冲区复制到提供的事件中。
关于如何在各种工具中使用Disruptor作为项目的一部分的几个例子。
在你的项目中使用Disruptor
有几个选项可用于在您的一个项目中使用破坏者作为依赖。这些是关于如何将它与各种构建系统结合使用的几个提示和代码。
手工依赖管理
包含编译的类,源和javadoc文档的JAR文件可从。您可以使用这些与任何种类的构建系统。
使用maven中央存储库的依赖管理
DisruptorJAR(工件)可从maven中央存储库从2.7.1版本获得。Disruptor使用组ID:com.googlecode.disruptor和artifact id:disruptor。
以下是在流行构建系统中使用的代码片段:
Maven
将以下代码片段添加到pom.xml文件的依赖关系部分
com.googlecode.disruptordisruptor2.7.1
Gradle
使用maven中央存储库启用依赖关系解析:
repositories {mavenCentral()}
然后添加破坏者到依赖关系部分:dependencies {compile "com.googlecode.disruptor:disruptor:2.7.1"}
Ivy
添加以下行到ivy.xml:
Disruptor向导
如何使用Disruptor DSL简化环路缓冲区的设置。
介绍
Disruptor类提供了一个简单的DSL风格的API,使其更容易设置事件处理程序并表达它们之间的依赖关系。
并行事件处理程序
首先使用所需的环形缓冲区配置创建向导:
Disruptor disruptor =new Disruptor(ValueEvent.EVENT_FACTORY, EXECUTOR,new SingleThreadedClaimStrategy(RING_SIZE),new SleepingWaitStrategy());
请注意,我们传递一个Executor实例,该实例将用于在自己的线程中执行事件处理程序。
然后我们添加将并行处理事件的事件处理程序:
disruptor.handleEventsWith(handler1, handler2, handler3, handler4);
最后启动事件处理程序线程并检索配置的RingBuffer:
RingBuffer ringBuffer = disruptor.start();
然后,生产者可以使用RingBuffer的nextEvent并发布函数,以将事件添加到环形缓冲区。
依赖
处理程序之间的依赖关系可以通过将它们链接在一起来表示在Disruptor中,例如:
disruptor.handleEventsWith(handler1).then(handler2, handler3, handler4);
在这种情况下,处理程序1必须首先处理事件,处理程序2,3和4之后并行处理它们。也可以创建依赖关系链,以确保每个处理程序按顺序处理事件:
disruptor.handleEventsWith(handler1).then(handler2).then(handler3).then(handler4);
还可以创建多个链:
disruptor.handleEventsWith(handler1).then(handler2);disruptor.handleEventsWith(handler3).then(handler4);
使用自定义事件处理器
Disruptor最常见的用法是提供一个EventHandler并让Disruptor自动创建一个BatchEventProcessor实例。在BatchEventProcessor的行为不合适的情况下,可以使用其他类型的EventProcessor作为依赖关系链的一部分。
设置自定义事件处理器以处理来自环形缓冲区的事件:
RingBuffer ringBuffer = disruptor.getRingBuffer();SequenceBarrier barrier = ringBuffer.newBarrier();final MyEventProcessor customProcessor = new MyEventProcessor(ringBuffer, barrier);disruptor.handleEventsWith(processor);disruptor.start();
当调用start()方法时,Disruptor将执行自定义处理器。然后在BatchEventHandler之前要求自定义处理器处理事件:
disruptor.after(customProcessor).handleEventsWith(anEventHandler);
或者,要使BatchEventHandler在自定义处理器之前处理事件,可以从Disruptor创建SequenceBarrier:
SequenceBarrier barrier = disruptor.after(batchEventHandler1, batchEventHandler2).asBarrier();final MyEventProcessor customProcessor = new MyEventProcessor(ringBuffer, barrier);
出版活动
Disruptor提供了一种方便的方法,使发布事件更简单-环境缓冲区--BayEvent(EventTranslator)。例如,发布者可以写成:public
class MyPublisher实现EventTranslator,Runnable {private
Object calculatedValue;私人破坏者破坏者;
public MyPublisher(Disruptor disruptor){this.disruptor = disruptor;}public void run(){while (true){computedValue = doLongRunningComputation();disruptor.publishEvent(this);}}public void translateTo(MyEvent event, long sequence){event.setComputedValue(computedValue);}private Object doLongRunningComputation(){...}}