С моделью памяти Java, возможно ли, что процесс выполнения потока увидит эффект своей локальной, несинхронизированной записи, хотя касание произошло позже?
Я вижу здесь тонкое, но важное упрощение: как, по вашему мнению, вы определяете, что действие записи вторым потоком произошло точно между действиями записи и чтения первым потоком? Чтобы иметь возможность сказать это, вам нужно будет предположить, что действие записи является идеализированной точкой на временной линии и что все записи происходят в последовательном порядке. На самом аппаратном обеспечении запись представляет собой очень сложный процесс, включающий в себя конвейер процессора, хранилища буферов, кеш L1, кэш второго уровня, шину на передней панели и, наконец, ОЗУ. Такой же процесс происходит одновременно на всех ядрах ЦП. Итак, что именно вы подразумеваете под одной записью «происходит после» другой?
Далее, рассмотрят "Roach Motel" парадигму, которая, кажется, помочь многим людям в качестве своего рода «ментального ярлыка» в последствия модели памяти Java: код легально может быть преобразован в
public void process() {
doStuff();
synchronized(this) {
someoneTouchedMeWhenIWasWorking = false;
if (someoneTouchedMeWhenIWasWorking) {
System.out.println("Hey!");
}
}
}
Этом это совершенно другой подход к использованию свобод, предоставляемых моделью памяти Java, для повышения производительности. Чтение внутри if-предложения действительно потребует считывания фактического адреса памяти, в отличие от прямого вложения значения false
и, следовательно, удаления всего if-блока (это действительно произойдет без), но запись false
не обязательно пишите в ОЗУ. На следующем этапе рассуждения оптимизирующий компилятор может решить удалить назначение false
в целом. В зависимости от особенностей всех других частей кода это одна из вещей, которые могут произойти, но есть много других возможностей.
Главное сообщение, которое нужно убрать из вышеизложенного, должно быть: не притворяйтесь, что можете уклониться от кода Java, используя некоторую упрощенную концепцию «локального кэширования»; вместо этого придерживайтесь официальной модели памяти Java и рассматривайте все доступные ей свободы.
Необходимо ли синхронизировать первую запись или сделать переменную изменчивой?
С учетом сказанного выше, я надеюсь, вы осознаете, что этот вопрос фактически не имеет никакого вещества. Вы будете либо наблюдать за записью другой нити, и, таким образом, гарантируете, что будете соблюдать все другие действия, которые она выполняла до написания, или вы не заметите этого и не получите гарантий. Если ваш код свободен от гонок данных, любой результат будет работать для вас. Если у него есть расы данных, тогда лучше исправить это, вместо того чтобы пытаться исправить вашу идиому.
Наконец, представьте, что ваша переменная изменчива. Что это даст вам, что у вас нет сейчас? В терминах формализма JMM две записи будут иметь определенный порядок между ними (наложенный общим порядком синхронизации), но вы будете в том же положении, что и вы сейчас: этот определенный порядок будет произвольным в любых конкретных выполнение.
Вопрос не в том, как сделать код безопасным, но безопасен ли он так, как есть? – icza
Синхронизированный блок в процессе() гарантирует, что последнее значение из основной памяти будет поднято. Следовательно, код безопасен, как есть. –