Как Akka реализует JMM-подобную ситуацию - до отношений?

В официальном документе Akka они отказываются:

Чтобы предотвратить проблемы с видимостью и переупорядочением на актеров, Akka гарантирует следующие два правила "произойдет до":

Правило отправки актера: передача сообщения актеру происходит до получения этого сообщения одним и тем же актером. Актер следующее правило обработки: обработка одного сообщения происходит до обработка следующего сообщения тем же актером.

Подробнее doc.

Мне интересно, как это делает Akka. Я только что просмотрел исходный код (последний в данный момент), и я думал, что перед выполнением Actor.receive должен быть Lock, но я не нашел блокировок (я думаю). Наконец, я нашел комментарий для ActorCell.invoke:

//Консистенция памяти обрабатывается почтовым ящиком (чтение почтового ящика статус, затем обрабатывать сообщения, а затем записывать статус почтового ящика

Да, Mailbox.status, я думаю, это то, что я ищу. Я видел, что они использовали Unsafe для доступа/обновления поля status, но я просто не мог понять, как это может обеспечить видимость памяти.

Ответ 1

Можно рассмотреть две вещи: передать сообщение и правильно опубликовать внутреннее состояние актеров.

Первая достигается реализацией MessageQueue почтовых ящиков, которая будет использовать волатильные записи (для ConcurrentLinkedQueue по умолчанию) или блокировки (для обычного LinkedBlockingQueue), чтобы обеспечить безопасную публикацию объекта, помещенного в очередь. Актер будет синхронизироваться с отправителем, читая те же самые изменчивые поля (в первом случае) или беря одни и те же блокировки (во втором), поэтому все записи до отправки сообщения случаются - перед чем-либо внутри актера при обработке этого сообщения.

Внутреннее состояние актеров безопасно убирается, даже когда оно переносится по другому потоку по статусу почтового ящика, который вы нашли: после обработки пакета сообщений (определенного параметром throughput) почтовый ящик установлен на "нет" запланированный "статус, который является volatile write (фактически Unsafe.compareAndSetInt(), который имеет ту же семантику). Перед тем, как актер начинает обрабатывать сообщения, он считывает статус почтового ящика с помощью Unsafe.getIntVolatile, который синхронизируется с предыдущей записью, поэтому все записи, сделанные актером во время последней серии сообщений, происходят до того, как все прочитает во время этой партии.

Подробнее о семантике задействованных операций можно прочитать здесь, имея в виду, что методы *Volatile на sun.misc.Unsafe подчиняются тем же правилам, что и для Atomic*Reference.