Какова причина волатильной семантики в Java и С#

Оба С# и Java определяют, что * volatile reads приобретают семантику
* volatile write имеет семантику выпуска



Мои вопросы:

  • Это единственный правильный способ определения изменчивости.
  • Если нет, все будет ужасно иначе, если семантика будет отменена, то есть
    • volatile reads имеет семантику выпуска
    • volatile пишет наличие семантики

Ответ 1

Обоснование семантики volatile коренится в Java Memory Model, которая указана в терминах действий:

  • читает и записывает в переменные
  • блокировки и разблокировки мониторов
  • начало и соединение с потоками

Модель памяти Java определяет частичное упорядочение, называемое происходит-до для действий, которые могут возникать в Java программа. Обычно нет гарантии, что потоки могут видеть результаты друг друга.

Скажем, у вас есть два действия A и B. Чтобы гарантировать, что поток, выполняющий действие B, может видеть результаты действия A, должно быть отношение происходит до между A и B. Если нет, JVM может быть переупорядочивать их по своему усмотрению.

Программа, которая неправильно синхронизирована, может иметь расы данных. Готовность данных происходит, когда переменная считывается с помощью > 1 потоков и записывается с помощью >= 1 потока (-ов), но операции чтения и записи не упорядочиваются с помощью упорядоченного порядка.

Следовательно, правильно синхронизированная программа не имеет расов данных, и все действия внутри программы происходят в фиксированном порядке.

Таким образом, действия обычно упорядочены только частично, но также существует общий порядок:

  • фиксация и выпуск блокировки
  • читает и записывает переменные volatile

Эти действия полностью упорядочены.

Это делает разумным описать случившееся - раньше в терминах "последующих" блокировок и чтения изменчивых переменных.

Относительно ваших вопросов:

  • С отношениями "бывшее-до" у вас есть альтернативное определение volatile
  • Реверсирование порядка не имеет смысла для определения выше, тем более, что существует общий порядок.

happens-before

Это иллюстрирует связь между событиями, когда два потока синхронизируются с использованием общей блокировки . Все действия в потоке A упорядочены по правилу заказа программы, равно как и действия в потоке B. Поскольку A освобождает блокировку M и B впоследствии, получает M, все действия в перед освобождением блокировки, поэтому упорядочен до действий в B после приобретения блокировки. Когда два потока синхронизируются с разными блокировками, мы ничего не можем сказать о упорядочении действий между ними, так как это не происходит - до отношения между действиями в двух потоках.

Источник: Java Concurrency на практике

Ответ 2

Синтаксис семантики получения/выпуска заключается не столько в том, как скоро другие потоки видят недавно записанное значение самого изменчивого поля, а скорее в том, как изменчивые операции устанавливают связь между событиями перед разными потоками. Если поток A считывает изменчивое поле и видит значение, которое было записано в это поле в другом потоке B, тогда поток A также гарантированно будет видеть значения, записанные в другие (не обязательно изменчивые) переменные потоком B до того момента, когда он выполнил volatile write. Это похоже на очистку кеша, но только с точки зрения потока, который читает изменчивые, другие потоки, которые не касаются изменчивого поля, не имеют гарантий порядка в отношении B и могут видеть некоторые из его более ранних нелетучих записей, но а не другие, если компилятор /JIT настолько наклонен.

Наблюдение за мониторами/выпусков аналогично характеризуется их вызванным случаем - перед отношением - действие одним потоком до того, как освобождение монитора будет гарантировано быть видимым после последующего приобретения того же монитора другим потоком. Волатильцы дают вам одинаковые гарантии заказа, такие как синхронизация монитора, но без блокировки.