Когда я просмотрел статью о DOUBLE-CHECKED LOCKING на http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking--clever--but-broken.html, я столкнулся с комментарием, в котором говорится: «Следует отметить, что DCL может, по сути, работать с некоторыми версиями некоторых JVM, поскольку только несколько JVM фактически реализуют JMM правильно. " Итак, я выводю из него, что JMM указывает, что синхронизированный блок является атомарным, даже если блоки не синхронизированы в других потоках. Я прав? (Я пытался прочитать JMM на веб-сайте oracle, но это было слишком абстрактно, и я сдался.)Определяет ли последний JMM синхронизированный блок как атомарный для других потоков, даже асинхронный?
ответ
Прежде всего, обратите внимание, что Брайан Гетц написал эту статью в 2001 году. Информация, изображаемая в этом документе статья не является более точной после implementation of JSR-133, пересмотренной модели памяти. Что, однако, правда, в том, что пример DCL статьи сломана:
class SomeClass {
private Resource resource = null;
public Resource getResource() {
if (resource == null) {
synchronized (this) {
if (resource == null)
resource = new Resource();
}
}
return resource;
}
}
Используя приведенный выше код, то resource
поле может наблюдаться быть не null
в то время как конструктор экземпляра была еще не в полном объеме. Проблема заключается в том, что конструктор не гарантированно подвергается экспансии перед назначением поля, поскольку JVM может применять оптимизацию кода. Вызов конструктора должен поэтому скорее следует рассматривать как (в псевдокоде):
resource = alloc Resource;
resource.new();
С этой информацией, можно увидеть, как начальная проверка resource == null
может дать false
другой поток еще до new
называется то, что подвергает неполную экземпляр в другой поток. Этот другой поток никогда не войдет в синхронизированный блок и не дожидается завершения вызова конструктора.
В современной Java, однако, достаточно, чтобы поле resource
было volatile
. В этом случае DCL работает и даже достаточно эффективен, поскольку чтение изменчивого поля - not too expensive on most hardware. Алексей Шипилев обсудил результаты работы безопасного, ленивого издания in detail. DCL с volatile
является распространенным образцом сегодня, например, используется Scala для его полей lazy
.
Но для ответа на ваш реальный вопрос: в основном все реализации JVM реализуют модель памяти в более свободном режиме, чем ее спецификация. Таким образом, энергонезависимый DCL может работать только на многих машинах, несмотря на неправильную синхронизацию из-за детализации реализации. Однако вы никогда не должны кодировать реализацию, но всегда против спецификации. В противном случае ваш код может терпеть неудачу только иногда и только на некоторых машинах, что является ужасной ошибкой для отслеживания! Это не связано с тем, что блок synchronized
является атомарным, он связан только с тем, как ваша виртуальная машина выполняет ваш код, где конструктор может случайно всегда выполняться до публикации вашего экземпляра в поле resource
.
Синхронизированные блоки, безусловно, не являются атомарными, и я не вижу, как вы могли бы сделать вывод из этого комментария. –
Нет, вы ошибаетесь. Прочтите спецификацию JMM, если у вас есть вопрос об этом. Любой может придумать какие-то необоснованные предположения, но вы не можете ожидать, что люди углублятся, чтобы опровергнуть это, если вы не потрудились прочитать то, о чем говорите –