Для чего используются ограждения памяти в Java? - PullRequest
18 голосов
/ 07 февраля 2020

При попытке понять, как SubmissionPublisher ( исходный код в Java SE 10, OpenJDK | docs ), новый класс добавлен в Java SE в версии 9, реализовано, я наткнулся на несколько вызовов API на VarHandle, о которых я раньше не знал:

fullFence, acquireFence, releaseFence, loadLoadFence и storeStoreFence.

Проведя некоторые исследования, особенно касающиеся концепции барьеров / ограждений памяти (да, я слышал о них ранее, да; но никогда не использовал их, поэтому совершенно не знаком с их семантикой) Я думаю, у меня есть базовое понимание того, для чего они нужны. Тем не менее, поскольку мои вопросы могут возникнуть из-за неправильного представления, я хочу убедиться, что я правильно понял в первую очередь:

  1. Барьеры памяти - это переупорядочивание ограничений в отношении операций чтения и записи.

  2. Барьеры памяти можно разделить на две основные категории: однонаправленные и двунаправленные барьеры памяти, в зависимости от того, устанавливают ли они ограничения для чтения или записи или для обоих.

  3. C ++ поддерживает различные барьеры памяти , однако они не совпадают с теми, которые предоставляются VarHandle. Однако некоторые из барьеров памяти, доступных в VarHandle, обеспечивают эффекты упорядочения , которые совместимы с соответствующими им барьерами памяти C ++.

    • #fullFence совместим с atomic_thread_fence(memory_order_seq_cst)
    • #acquireFence совместим с atomic_thread_fence(memory_order_acquire)
    • #releaseFence совместим с atomic_thread_fence(memory_order_release)
    • #loadLoadFence и #storeStoreFence не имеют совместимой счетной части C ++

Слово совместимый кажется здесь действительно важным, так как семантика явно различается, когда дело касается деталей , Например, все барьеры C ++ являются двунаправленными, тогда как барьеры Java не являются (обязательно).

Большинство барьеров памяти также имеют эффекты синхронизации. Они особенно зависят от используемого типа барьера и ранее выполненных инструкций барьера в других потоках. Поскольку полное значение, которое имеет инструкция для барьера, зависит от аппаратного обеспечения c, я буду придерживаться барьеров более высокого уровня (C ++). Например, в C ++ изменения, сделанные до выполнения команды барьера release , видны потоку, выполняющему инструкцию барьера acqu .

Верны ли мои предположения? Если да, то мои следующие вопросы:

  1. Барьеры памяти, доступные в VarHandle, вызывают какой-либо тип синхронизации памяти?

  2. Независимо от вызывают ли они синхронизацию памяти или нет, для чего могут быть полезны ограничения переупорядочения в Java? Модель памяти Java уже дает некоторые очень строгие гарантии в отношении упорядочения, когда задействованы изменчивые поля, блокировки или VarHandle операции, такие как #compareAndSet.

На случай, если вы ищете пример: вышеупомянутый BufferedSubscription, внутренний класс SubmissionPublisher (источник указан выше), установил полный забор в строке 1079 (функция growAndAdd; поскольку связанный веб-сайт не поддерживает идентификаторы фрагментов, просто CTRL + F для этого). Однако для меня неясно, для чего это.

1 Ответ

11 голосов
/ 08 февраля 2020

Это в основном не ответ, на самом деле (изначально хотел сделать это комментарием, но, как вы видите, это слишком долго). Просто я сам много сомневался в этом, много читал и исследовал, и на данный момент я могу с уверенностью сказать: это сложно. Я даже написал несколько тестов с помощью jcstress , чтобы выяснить, насколько они действительно работают (глядя на сгенерированный код сборки), и хотя некоторые из них так или иначе имеют смысл, тема в целом не значит легко.

Самое первое, что вам нужно понять:

В спецификации языка Java (JLS) нигде не упоминается барьеров . Для java это будет деталь реализации: она действительно действует в терминах , предшествующих семантике . Чтобы иметь возможность правильно указать их в соответствии с JMM (Java Модель памяти), JMM придется изменить довольно много .

Работа в процессе.

Во-вторых, если вы действительно хотите поцарапать поверхность, это самое первое, что нужно посмотреть . Разговор невероятный. Моя любимая часть - это когда Херб Саттер поднимает свои 5 пальцев и говорит: «Вот как много людей могут действительно и правильно с ними работать». Это должно дать вам подсказку о сложности. Тем не менее, есть несколько тривиальных примеров, которые легко обработать asp (например, счетчик, обновляемый несколькими потоками, который не заботится о других гарантиях памяти, но заботится только о том, чтобы он сам увеличивался правильно).

Другой пример - когда (в java) вы хотите, чтобы флаг volatile управлял потоками для остановки / запуска. Вы знаете, классический:

volatile boolean stop = false; // on thread writes, one thread reads this    

Если вы работаете с java, вы знаете, что без volatile этот код не работает (вы можете прочитать, почему двойная проверка блокировки сломан без него например). Но знаете ли вы, что для некоторых людей, которые пишут высокопроизводительный код, это слишком много? volatile чтение / запись также гарантирует последовательную согласованность - это имеет некоторые серьезные гарантии, и некоторые люди хотят более слабую версию этого.

Флаг безопасности потока, но не энергозависимый? Да, именно: VarHandle::set/getOpaque.

И вы бы спросили , почему кому-то это может понадобиться, например? Не всем интересны все изменения, которые поддерживаются volatile.

Давайте посмотрим, как мы достигнем этого в java. Прежде всего, такие exoti c вещей уже существовали в API: AtomicInteger::lazySet. Это не указано в модели памяти Java, а не имеет четкого определения ; все еще люди использовали это (LMAX, afaik или это для большего чтения ). ИМХО, AtomicInteger::lazySet - это VarHandle::releaseFence (или VarHandle::storeStoreFence).


Давайте попробуем ответить зачем кому-то нужны эти ?

JMM имеет два основных способа доступа к полю: обычный и изменчивый (что гарантирует последовательную согласованность ). Все эти методы, о которых вы упомянули, предназначены для того, чтобы что-то между этими двумя - освободить / приобрести семантику ; Я полагаю, есть случаи, когда люди на самом деле нуждаются в этом.

Даже больше расслабления от выпуска / приобретения будет непрозрачным , что Я все еще пытаюсь полностью понять,


Таким образом, практический результат (ваше понимание довольно правильно, кстати): если вы планируете использовать это в java - у них нет спецификаций на данный момент, делайте это на свой страх и риск. Если вы действительно хотите понять их, их режимы C ++ эквивалентны для начала.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...