Java Synchronized ключевое слово промота кеша?
-
25-09-2019 - |
Вопрос
Java 5 и выше только. Предположим, что многопроцессорный компьютерный компьютер (вы, вероятно, используете один прямо сейчас).
Вот код для ленивой инициализации синглтона:
public final class MySingleton {
private static MySingleton instance = null;
private MySingleton() { }
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
if (instance == null) {
instance = new MySingleton();
}
}
}
return instance;
}
}
Делает instance
должны быть объявлены volatile
Чтобы предотвратить перезапись оптимизатора () следующим образом () следующим образом (что было бы правильно в последовательной программе):
public static MySingleton getInstance() {
if (instance == null) {
synchronized (MySingleton.class) {
// instance must be null or we wouldn't be here (WRONG!)
instance = new MySingleton();
}
}
}
Предполагая, что оптимизатор не переписывает код, если instance
не объявлен volatile
это все еще гарантированно промыты в память, когда synchronized
блок выходит и читается из памяти, когда synchronized
Блок вводится?
Редактировать: я забыл сделать GetSstance () статическими. Я не думаю, что меняет действительность ответов; Вы все знали, что я имел в виду.
Решение
Да, instance
должен быть объявлен volatile
. Отказ Даже тогда рекомендуется не использовать двойной проверку. Это (или быть точным, модель памяти Java), используемая для серьезного недостатка, которая разрешена публикация частично реализованных объектов. Это было зафиксировано в Java5, все еще DCL является устаревшей идиомой и больше не нужно его использовать - используйте Держатель ленивого инициализации IDIOM вместо.
От Java параллелизм на практике, Раздел 16.2:
Настоящая проблема с DCL является предположение, что худшее, что может произойти при чтении общего эталона без синхронизации - это ошибочно видеть устаревшее значение (в этом случае,
null
); В этом случае DCL IDIOM компенсирует этот риск, пытаясь снова с замком. Но худший случай на самом деле значительно хуже - можно увидеть текущее значение эталона, но устаревшие значения для состояния объекта, что означает, что объект можно увидеть в неверном или неправильном состоянии.Последующие изменения в JMM (Java 5.0 и позже) включили DCL для работы если
resource
сделанvolatile
, и воздействие производительности это мало, так какvolatile
Читает, как правило, чуть дороже, чем нетолюбильные читания. Тем не менее, это идиома, утилита которой в значительной степени проходила - силы, которые мотивировали его (медленная недопустимая синхронизация, медленный запуск JVM) больше не в игре, что делает его менее эффективным в качестве оптимизации. Держатель ленивого инициализации IDIOM предлагает те же преимущества и легче понять.
Другие советы
Да, экземпляр должен быть волатильный, используя двойной проверенной блокировки в Java, потому что инициализация mysingleton может разоблачить частично построенный объект для остальной части системы. Также верно, что потоки синхронизируются, когда они достигают «синхронизированного» оператора, но это слишком поздно в этом случае.
Википедия И пару других вопросов переполнения стека есть хорошие дискуссии о «двойной проверке блокировки», поэтому я советую прочитать об этом. Я также советую не использовать его, если профилирование не показывает реальную потребность в производительности в этом конкретном коде.
Существует безопасная и читаемая идиома для ленивой инициализации Java Singletons:
class Singleton {
private static class Holder {
static final Singleton instance = create();
}
static Singleton getInstance() {
return Holder.instance;
}
private Singleton create() {
⋮
}
private Singleton() { }
}
Если вы используете более подробный двухместный заблокированный шаблон, вы должны объявить поле volatile
, как и другие уже отмечали.
Нет, это не должно быть нестабильным. Видеть Как решить «двойное заблокированное блокировка» сломана «Декларация» в Java?
Предыдущие попытки все потерпели неудачу, потому что если вы сможете обмануть Java и избежать волатильной / синхронизации, Java может обмануть вас и дать вам неверный вид на объект. Однако новый final
семантика решает проблему. Если вы получите ссылку на объект (обыденным чтением), вы можете безопасно прочитать его final
поля.