Ich habe mir aus purer Neugier die Zeit genommen, die eigentliche Quelle zu studieren, und die Idee dahinter ist recht einfach. Die aktuellste Version zum Zeitpunkt des Schreibens dieses Beitrags ist 3.2.1.
Es gibt einen Puffer, in dem vorab zugewiesene Ereignisse gespeichert sind, die die Daten enthalten, die die Verbraucher lesen können.
Der Puffer wird durch ein Array von Flags (Integer-Array) seiner Länge gesichert, das die Verfügbarkeit der Puffersteckplätze beschreibt (Einzelheiten siehe weiter unten). Auf das Array wird wie auf ein Java # AtomicIntegerArray zugegriffen. Für die Zwecke dieser Erläuterung können Sie also auch davon ausgehen, dass es sich um eines handelt.
Es kann eine beliebige Anzahl von Herstellern geben. Wenn der Produzent in den Puffer schreiben möchte, wird eine lange Nummer generiert (wie beim Aufrufen von AtomicLong # getAndIncrement verwendet der Disruptor tatsächlich seine eigene Implementierung, funktioniert jedoch auf die gleiche Weise). Nennen wir dies lange eine ProducerCallId. In ähnlicher Weise wird eine consumerCallId generiert, wenn ein Consumer einen Slot aus einem Puffer liest. Auf die neueste consumerCallId wird zugegriffen.
(Wenn es viele Verbraucher gibt, wird der Anruf mit der niedrigsten ID ausgewählt.)
Diese IDs werden dann verglichen, und wenn der Unterschied zwischen den beiden geringer ist als die Pufferseite, darf der Produzent schreiben.
(Wenn die ProducerCallId größer als die aktuelle ConsumerCallId + bufferSize ist, bedeutet dies, dass der Puffer voll ist und der Produzent gezwungen ist, mit dem Bus zu warten, bis ein Spot verfügbar ist.)
Dem Produzenten wird dann der Steckplatz im Puffer basierend auf seiner callId (prducerCallId modulo bufferSize) zugewiesen. Da die bufferSize jedoch immer eine Potenz von 2 ist (bei der Puffererstellung erzwungenes Limit), wird als aktueller Vorgang ProducerCallId & (bufferSize - 1 verwendet )). Es ist dann frei, das Ereignis in diesem Slot zu ändern.
(Der eigentliche Algorithmus ist etwas komplizierter und umfasst zu Optimierungszwecken das Zwischenspeichern der aktuellen Verbraucher-ID in einer separaten Atomreferenz.)
Wenn das Ereignis geändert wurde, wird die Änderung "veröffentlicht". Beim Veröffentlichen wird der jeweilige Slot im Flag-Array mit dem aktualisierten Flag gefüllt. Der Flag-Wert ist die Nummer der Schleife (ProducerCallId geteilt durch BufferSize (da BufferSize die Potenz 2 ist, ist die eigentliche Operation eine Rechtsverschiebung).
In ähnlicher Weise kann es eine beliebige Anzahl von Verbrauchern geben. Jedes Mal, wenn ein Verbraucher auf den Puffer zugreifen möchte, wird eine consumerCallId generiert (abhängig davon, wie die Verbraucher zum Disruptor hinzugefügt wurden, kann das bei der ID-Generierung verwendete Atom für jeden von ihnen gemeinsam genutzt oder getrennt werden). Diese ConsumerCallId wird dann mit der neuesten producentCallId verglichen, und wenn sie geringer ist, kann der Leser Fortschritte machen.
(Wenn die ProducerCallId sogar der ConsumerCallId entspricht, bedeutet dies, dass der Puffer leer ist und der Consumer zum Warten gezwungen wird. Die Art des Wartens wird durch eine WaitStrategy während der Disruptor-Erstellung definiert.)
Für einzelne Verbraucher (diejenigen mit eigenem ID-Generator) wird als nächstes die Fähigkeit zum Batch-Verbrauch überprüft. Die Slots im Puffer werden in der Reihenfolge von der jeweiligen zur ConsumerCallId (der Index wird auf die gleiche Weise wie für Produzenten bestimmt) bis zu der Slote zur aktuellen ProducerCallId untersucht.
Sie werden in einer Schleife untersucht, indem der im Flag-Array geschriebene Flag-Wert mit einem für die consumerCallId generierten Flag-Wert verglichen wird. Wenn die Flags übereinstimmen, bedeutet dies, dass die Produzenten, die die Slots füllen, ihre Änderungen festgeschrieben haben. Wenn nicht, wird die Schleife unterbrochen und die höchste festgeschriebene Änderungs-ID zurückgegeben. Die Slots von ConsumerCallId bis in changeId empfangen können im Stapel verwendet werden.
Wenn eine Gruppe von Verbrauchern zusammen liest (diejenigen mit gemeinsam genutztem ID-Generator), nimmt jeder nur eine einzelne Anruf-ID entgegen, und nur der Steckplatz für diese einzelne Anruf-ID wird überprüft und zurückgegeben.