Właściwie poświęciłem czas na zbadanie rzeczywistego źródła, z czystej ciekawości, a pomysł, który za tym stoi, jest dość prosty. Najnowsza wersja w momencie pisania tego postu to 3.2.1.
Istnieje bufor przechowujący wstępnie przydzielone zdarzenia, które będą przechowywać dane do odczytania przez konsumentów.
Bufor jest wspierany przez tablicę flag (tablica liczb całkowitych) o jego długości, która opisuje dostępność miejsc na bufory (więcej szczegółów w dalszej części). Dostęp do tablicy jest podobny do java # AtomicIntegerArray, więc dla celów tego wyjaśnienia równie dobrze możesz założyć, że jest ona jedna.
Może być dowolna liczba producentów. Gdy producent chce zapisać do bufora, generowana jest długa liczba (jak podczas wywoływania AtomicLong # getAndIncrement, Disruptor faktycznie używa własnej implementacji, ale działa w ten sam sposób). Nazwijmy to generowane długo nazwą producenta CallCd. W podobny sposób generowany jest konsumentCallId, gdy konsument KONIEC odczytuje boks z bufora. Dostęp do najnowszego adresu konsumenta jest możliwy.
(Jeśli jest wielu konsumentów, wybierane jest połączenie o najniższym identyfikatorze).
Te identyfikatory są następnie porównywane, a jeśli różnica między nimi jest mniejsza niż po stronie bufora, producent może pisać.
(Jeśli wartość parametru ProducCallId jest większa niż najnowszy rozmiar ConsumerCallId + bufferSize, oznacza to, że bufor jest pełny, a producent jest zmuszony czekać na magistrali, aż miejsce stanie się dostępne).
Producentowi zostaje następnie przypisane miejsce w buforze na podstawie jego callId (którym jest prducerCallId modulo bufferSize, ale ponieważ rozmiar bufferSize jest zawsze potęgą 2 (limit wymuszony przy tworzeniu bufora), zastosowana operacja aktuall to producentCallId & (bufferSize - 1 )). Następnie można modyfikować wydarzenie w tym gnieździe.
(Rzeczywisty algorytm jest nieco bardziej skomplikowany i wymaga buforowania ostatniego konsumenta w osobnym odwołaniu atomowym w celu optymalizacji).
Gdy wydarzenie zostało zmodyfikowane, zmiana jest „publikowana”. Podczas publikowania odpowiedni slot w tablicy flag jest wypełniony zaktualizowaną flagą. Wartością flagi jest numer pętli (producentCallId podzielony przez bufferSize (ponownie, ponieważ bufferSize to potęga 2, faktyczna operacja to przesunięcie w prawo).
W podobny sposób może być dowolna liczba konsumentów. Za każdym razem, gdy konsument chce uzyskać dostęp do bufora, generowany jest identyfikator konsumenta Callall (w zależności od tego, w jaki sposób konsumenci zostali dodani do elementu zakłócającego, atom używane do generowania identyfikatorów może być współdzielone lub oddzielne dla każdego z nich). Ten ConsumerCallId jest następnie porównywany z najnowszym producentemCallId, a jeśli jest mniejszy z tych dwóch, czytelnik może robić postępy.
(Podobnie, jeśli producentCallId jest nawet w stosunku do konsumentaCallId, oznacza to, że bufor jest empety i konsument jest zmuszony czekać. Sposób oczekiwania jest definiowany przez WaitStrategy podczas tworzenia zakłócacza).
W przypadku indywidualnych konsumentów (tych z własnym generatorem identyfikatorów) następną sprawą jest możliwość konsumpcji partii. Miejsca w buforze są sprawdzane w kolejności od tej odpowiadającej konsumentowi CallCd (indeks jest ustalany w taki sam sposób jak dla producentów), do tej odpowiadającej najnowszemu producentowi CallCall.
Są one badane w pętli poprzez porównanie wartości flagi zapisanej w tablicy flag, z wartością flagi wygenerowaną dla ConsumerCallId. Jeśli flagi się zgadzają, oznacza to, że producenci wypełniający automaty dokonali zmian. Jeśli nie, pętla jest przerywana i zwracana jest najwyższa zatwierdzona zmiana Id. Automaty od ConsumerCallId do otrzymanych w changeId można wykorzystać wsadowo.
Jeśli grupa konsumentów będzie czytać razem (ci ze wspólnym generatorem identyfikatora), każdy z nich przyjmuje tylko jedno callId, a tylko miejsce dla tego pojedynczego callId jest sprawdzane i zwracane.