TL;DR: Друзья, не позволяйте друзьям тратить время на выяснение того, работает ли колоритный доступ в соответствии с желаниями Extreme Concurrency Optimist. Используйте volatile
и спите спокойно.
Я смотрю на первую таблицу в поваренной книге JSR-133.
Обратите внимание, что полное название — «JMM Cookbook For Compiler Writers». Напрашивается вопрос: мы здесь создатели компиляторов или просто пользователи, пытающиеся разобраться в нашем коде? Я думаю, что последнее, поэтому нам действительно следует закрыть JMM Cookbook и открыть сам JLS. См. "Миф: Поваренная книга JSR 133 JMM Synopsis" и следующий раздел.
Другими словами, может ли get() вернуть null?
Да, тривиально, get()
наблюдая за значениями по умолчанию для полей, не наблюдая за тем, что делал change()
. :)
Но я предполагаю, что вопрос в том, будет ли разрешено видеть старое значение в m_volatile
после завершения change()
(Предостережение: для некоторого понятия «завершено», потому что это подразумевает время, а логическое время указывается самим JMM).
В основном вопрос заключается в том, существует ли допустимое выполнение, включающее read(m_normal):null --po/hb--> read(m_volatile):null
, с чтением m_normal
, наблюдающим за записью null
в m_normal
? Да, вот оно: write(m_volatile, X) --po/hb--> write(m_normal, null) ... read(m_normal):null --po/hb--> read(m_volatile):null
.
Чтение и запись в m_normal
не упорядочены, поэтому нет структурных ограничений, запрещающих выполнение, считывающее оба нуля. Но «летучий», скажете вы! Да, это связано с некоторыми ограничениями, но не в том порядке, в котором они были. энергонезависимые операции см. "Подводный камень: приобретение и освобождение в неправильном порядке" (внимательно посмотрите на этот пример, он удивительно похож на то, о чем вы спрашиваете).
Это правда, что операции с самим m_volatile
обеспечивают некоторую семантику памяти: запись в m_volatile
— это «выпуск», который «опубликует» все, что произошло до него, а чтение из m_volatile
— это «получение», которое «получает» все опубликованное. Если вы точно сделаете вывод, как в этом посте, появится шаблон: вы можете тривиально переместить операции над программой «выпуск» вверх (в любом случае это было колоритно!), а вы можете тривиально переместите операции над программой «acquire» вниз (это тоже было пикантно!).
Эту интерпретацию часто называют "семантика мотеля тараканов", и она дает интуитивно понятный ответ. to: "Можно ли изменить порядок этих двух утверждений?"
m_volatile = value; // release
m_normal = null; // some other store
Ответ в соответствии с семантикой мотеля тараканов - «да».
Каков наилучший способ решить эту проблему?
Лучший способ решить эту проблему — с самого начала избегать колоритных операций и, таким образом, избежать всей неразберихи. Просто сделайте m_normal
volatile
, и все готово: операции над m_normal
и m_volatile
будут последовательно согласованы.
Будет ли добавлено значение = m_volatile; после m_volatile = значение; предотвратить назначение m_normal до назначения m_volatile?
Так что вопрос в том, поможет ли это:
m_volatile = value; // "release"
value = m_volatile; // poison "acquire" read
m_normal = null; // some other store
В наивном мире только семантики мотеля тараканов это могло бы помочь: казалось бы, как будто яд ломает код движения. Но, поскольку значение этого чтения не наблюдается, оно эквивалентно выполнению без какого-либо подозрительного чтения, и хорошие оптимизаторы воспользуются этим. См. "Принятие желаемого за действительное: ненаблюдаемые летучие вещества имеют эффект памяти" "а>. Важно понимать, что переменные не всегда означают барьеры, даже если они есть в консервативной реализации, описанной в JMM Cookbook for Compiler Writers.
В стороне: есть альтернатива, VarHandle.fullFence()
, которую можно использовать в подобном примере, но она ограничена очень влиятельными пользователями, потому что рассуждения с барьерами становятся граничащими с безумием. См. "Миф: барьеры — это нормально Ментальная модель" и «Миф: переупорядочение и запоминание».
Просто сделай m_normal
volatile
, и всем будет лучше спать.
person
Aleksey Shipilev
schedule
12.02.2018