Какие методы синхронизации JVM можно игнорировать, полагая, что я знаю, что буду работать на процессоре x64?

Я знаю, что модель памяти JVM создана для самого низкого общего знаменателя процессоров, поэтому она должна принять самую слабую возможную модель процессора, на котором может работать JVM (например, ARM).

Теперь, учитывая, что x64 имеет довольно сильную модель памяти, какие методы синхронизации можно игнорировать, полагая, что моя программа будет работать только на 64-битных процессорах x86? Также это применимо, когда моя программа запускается через виртуализацию?

Пример:
Известно, что для модели памяти JVM требуется синхронизировать доступ чтения/записи к длинным и удвоенным, но можно предположить, что чтение/запись других 32-битных примитивов, таких как int, float и т.д., Являются атомарными.

Однако, если я знаю, что я работаю на 64-битной машине x86, могу ли я игнорировать использование блокировок на longs/doubles, зная, что процессор будет читать/записывать 64-битные значения и просто сохранять их волатильными (например, с Ints/поплавки)?

Ответ 1

Я знаю, что модель памяти JVM создана для самого низкого общего знаменателя процессоров, поэтому она должна принять самую слабую возможную модель процессора, на котором может работать JVM (например, ARM).

Это не правильно. JMM был результатом компромисса между различными конкурирующими силами: стремление к более слабой модели памяти, чтобы программы могли быстрее работать на аппаратных средствах, которые имеют слабые модели памяти; желание авторов компилятора, которые хотят, чтобы определенные оптимизации были разрешены; и стремление к тому, чтобы результаты параллельных Java-программ были правильными и предсказуемыми, и, если возможно, (!) понятными для Java-программистов. См. статью Sarita Adve CACM для общего обзора проблем с моделью памяти.

Учитывая, что x64 имеет довольно сильную модель памяти, какие методы синхронизации можно игнорировать, если я знаю, что моя программа будет работать только на процессорах [x64]?

Отсутствует. Проблема в том, что модель памяти применяется не только к базовому оборудованию, но также относится к JVM, который выполняет вашу программу и, в основном, на практике, JVM JIT-компилятор. Компилятор может решить применить определенные оптимизации, которые допускаются в модели памяти, но если ваша программа делает необоснованные предположения о поведении памяти на основе базового оборудования, ваша программа сломается.

Вы спросили о x64 и атомной 64-разрядной записи. Может случиться так, что на машине x64 не будет разрыва слова. Я сомневаюсь, что любой компилятор JIT разорвал бы 64-битное значение на 32-битные записи в качестве оптимизации, но вы никогда не знаете. Однако маловероятно, что вы можете использовать эту функцию, чтобы избежать синхронизации или изменчивости полей в вашей программе. Без них записи в эти переменные никогда не станут видимыми для других потоков, или они могут быть произвольно переупорядочены относительно других записей, что может привести к ошибкам в вашей программе.

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

UPDATE

OP обновил вопрос, чтобы быть более конкретным относительно использования volatile вместо блокировок и синхронизации.

Оказывается, что volatile не только имеет семантику видимости памяти. Он также делает доступными атомы long и double, что не относится к переменным не volatile этих типов. См. Раздел JLS 17.7. Вы должны быть в состоянии полагаться на volatile, чтобы обеспечить атомарность на любом оборудовании, а не только на x64.

Пока я нахожусь на этом, для получения дополнительной информации о модели памяти Java см. статью о расширении использования JMM Pragmatics в журнале Алексея Шипилева . (Алексей также является JMH парнем.) Там много подробностей в этом разговоре и некоторые интересные упражнения, чтобы проверить одно понимание. Один общий вывод разговоров заключается в том, что часто ошибочно полагаться на одну интуицию о том, как работает модель памяти, например, в терминах строк кэша или буферов записи. JMM - это формализм в отношении операций с памятью и различных противопоказаний (синхронизация-с, происходит-прежде и т.д.), Которые определяют порядок этих операций. Это может иметь весьма противоречивые результаты. Неразумно пытаться перехитрить JMM, думая об определенных свойствах оборудования. Он вернется, чтобы укусить тебя.

Ответ 2

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

То, что я имею в виду здесь, например, в Oracle Java, большинство операций синхронизации на низком уровне заканчиваются в Unsafe (docjar.com/docs/api/sun/misc/Unsafe.html#getUnsafe), который, в свою очередь, имеет длинный список собственных методов. Таким образом, в конце концов, эти методы синхронизации и множество других операций низкого уровня инкапсулируются JVM, где они принадлежат. x64 не имеет того же jvm, что и x86.

после прочтения отредактированного вопроса еще раз: атомарность операций загрузки/хранения была здесь . Так что нет, вам не нужно беспокоиться об атомной 64-разрядной загрузке/хранении на x64. Но так как это не конец всех проблем синхронизации, см. Другие ответы.

Ответ 3

Всегда включайте барьеры памяти, в которых модель памяти JVM заявляет, что они необходимы, а затем пусть JVM оптимизирует их, когда это возможно для разных платформ.

Знание того, что вы запускаете только на процессорах x86, не означает, что вы можете отказаться от использования барьеров памяти. Если, возможно, вы не знаете, что вы будете работать только на одном ядре x86 cpus;) Который, в сегодняшнем многоядерном мире, которого никто не знает.

Почему? Потому что модель памяти Java имеет две основные проблемы.

  • видимость данных между ядрами и
  • происходит до гарантий, а также переупорядочивания.

Без барьера памяти в игре порядок операций, которые становятся видимыми для других ядер, может стать очень запутанным; и это даже с более сильными гарантиями, предлагаемыми x86. x86 обеспечивает согласованность только после того, как данные попадают в кеш-память cpu, и в то время как его гарантии порядка очень сильны, они только пинают один раз, когда Hotspot сообщает CPU, чтобы он записывал в кеш.

Без энергозависимой/синхронизированной информации, это будет до компиляторов (javac и hotspot) относительно того, когда они будут делать эти записи и в каком порядке. Для них совершенно справедливо принять решение хранить данные в течение длительных периодов в реестрах. Когда пересекается энергозависимый или синхронизированный барьер памяти, JVM знает, как заставить CPU отправлять данные в кеш.

Поскольку документы Doug Lea в JBL-133 Cookbook, большинство барьеров x86 сводятся к инструкциям no-op, которые гарантируют заказ. Таким образом, JVM сделает инструкции максимально эффективными для нас. Код в модель памяти Java, и пусть Hotspot работает над своей магией. Если Hotspot может доказать, что синхронизация не требуется, она может полностью отказаться от нее.

Наконец, двойная проверенная блокировка была доказана также для многоядерных x86; несмотря на более надежные гарантии памяти. Некоторая приятная деталь из этого была написана Бартосом Милевским на его блоге С++ здесь

Ответ 4

Записи компилятора Позаботились о том, что вы хотели сделать. Многие из неустойчивых барьеров чтения/записи в конечном итоге не будут работать на x64. Также подумайте, что переупорядочение также может быть вызвано из-за оптимизации компилятора и не может зависеть от оборудования. Для хороших гонок данных - например String hashCode. См.: http://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html

Также см. страницу, на которой инструкции могут быть без операций на x64. См. http://gee.cs.oswego.edu/dl/jmm/cookbook.html в разделе "Многопроцессоры".

Я советую не делать никаких оптимизаций для аппаратного обеспечения. Вы можете написать Недопустимый код. Сценаристы компилятора уже создали достаточно HardWork.

Ответ 5

Это зависит не только от процессора, но и от JVM, операционной системы и т.д.

Одно можно сказать наверняка: не предполагайте ничего, если дело доходит до синхронизации потоков.