En fait, j'ai pris le temps d'étudier la source réelle, par pure curiosité, et l'idée derrière cela est assez simple. La version la plus récente au moment de la rédaction de cet article est la 3.2.1.
Il existe un tampon stockant des événements pré-alloués qui contiendront les données à lire par les consommateurs.
Le tampon est soutenu par un tableau de drapeaux (tableau d'entiers) de sa longueur qui décrit la disponibilité des emplacements de tampon (voir plus loin pour plus de détails). Le tableau est accessible comme un java # AtomicIntegerArray, donc pour les besoins de cette explication, vous pouvez aussi supposer qu'il en est un.
Il peut y avoir n'importe quel nombre de producteurs. Lorsque le producteur souhaite écrire dans le tampon, un nombre long est généré (comme lors de l'appel à AtomicLong # getAndIncrement, le disrupteur utilise en fait sa propre implémentation, mais il fonctionne de la même manière). Appelons cela généré longtemps un producteurCallId. De manière similaire, un consumerCallId est généré lorsqu'un consommateur ENDS lit un slot dans un tampon. Le dernier ConsumerCallId est accessible.
(S'il y a beaucoup de consommateurs, l'appel avec l'ID le plus bas est choisi.)
Ces identifiants sont ensuite comparés, et si la différence entre les deux est moindre que le côté tampon, le producteur est autorisé à écrire.
(Si le producteurCallId est supérieur au récent consumerCallId + bufferSize, cela signifie que le tampon est plein et que le producteur est obligé d'attendre le bus jusqu'à ce qu'un spot soit disponible.)
Le producteur se voit alors attribuer l'emplacement dans le tampon en fonction de son callId (qui est prducerCallId modulo bufferSize, mais comme le bufferSize est toujours une puissance de 2 (limite appliquée à la création du tampon), l'opération actuall utilisée est le producteurCallId & (bufferSize - 1 )). Il est alors libre de modifier l'événement dans cet emplacement.
(L'algorithme réel est un peu plus compliqué, impliquant la mise en cache du consumerId récent dans une référence atomique distincte, à des fins d'optimisation.)
Lorsque l'événement a été modifié, le changement est "publié". Lors de la publication, l'emplacement respectif dans le tableau d'indicateurs est rempli avec l'indicateur mis à jour. La valeur de l'indicateur est le numéro de la boucle (producteurCallId divisé par bufferSize (encore une fois, puisque bufferSize est une puissance de 2, l'opération réelle est un décalage vers la droite).
De la même manière, il peut y avoir n'importe quel nombre de consommateurs. Chaque fois qu'un consommateur souhaite accéder au tampon, un consumerCallId est généré (en fonction de la façon dont les consommateurs ont été ajoutés au perturbateur, l'atomique utilisé dans la génération d'ID peut être partagé ou séparé pour chacun d'eux). Ce consumerCallId est ensuite comparé au plus récent producentCallId, et s'il est moindre des deux, le lecteur est autorisé à progresser.
(De même, si le producteurCallId est égal au consumerCallId, cela signifie que le tampon est vide et que le consommateur est obligé d'attendre. Le mode d'attente est défini par une WaitStrategy lors de la création du perturbateur.)
Pour les consommateurs individuels (ceux qui ont leur propre générateur d'ID), la prochaine chose vérifiée est la capacité de consommer par lots. Les créneaux dans le tampon sont examinés dans l'ordre de celui respectif au consumerCallId (l'indice est déterminé de la même manière que pour les producteurs), à celui respectif au producteurCallId récent.
Ils sont examinés en boucle en comparant la valeur d'indicateur écrite dans le tableau d'indicateurs, à une valeur d'indicateur générée pour le consumerCallId. Si les drapeaux correspondent, cela signifie que les producteurs remplissant les créneaux ont validé leurs modifications. Sinon, la boucle est rompue et le changeId engagé le plus élevé est renvoyé. Les emplacements de ConsumerCallId à reçus dans changeId peuvent être consommés par lot.
Si un groupe de consommateurs lisent ensemble (ceux avec un générateur d'ID partagé), chacun ne prend qu'un seul callId, et seul l'emplacement pour ce callId unique est vérifié et renvoyé.