Синхронизированное ключевое слово Java очищает кеш?
Только Java 5 и выше. Предположим, что многопроцессорный компьютер с общей памятью (вы, вероятно, используете его прямо сейчас).
Вот код для ленивой инициализации singleton:
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
, чтобы оптимизатор не переписывал getInstance() следующим образом (что было бы правильным в последовательной программе):
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
вводится?
EDIT: я забыл сделать getInstance() static. Я не думаю, что это изменяет правильность ответов; вы все знали, что я имел в виду.
Ответы
Ответ 1
Да, instance
должен быть объявлен volatile
. Даже тогда рекомендуется не использовать блокировку с двойной проверкой. Он (или, точнее, модель памяти Java) имел серьезный недостаток, который позволял публиковать частично реализованные объекты. Это было исправлено в Java5, но DCL - устаревшая идиома, и больше не нужно использовать его - вместо этого используйте ленивую идентификацию владельца инициализации.
От Java Concurrency на практике, раздел 16.2:
Реальная проблема с DCL - это предположение, что худшее, что может случиться при чтении ссылки на общий объект без синхронизации, - это ошибочно увидеть устаревшее значение (в данном случае null
); в этом случае идиома DCL компенсирует этот риск, снова попробовав блокировку. Но худший случай на самом деле значительно хуже - можно увидеть текущее значение ссылочных, но устаревших значений для состояния объекта, что означает, что объект может считаться недопустимым или неправильным.
Последующие изменения в JMM (Java 5.0 и новее) позволили DCL работать, если resource
сделано volatile
, а влияние производительности этого мало, так как volatile
чтение обычно немного дороже, чем энергонезависимая читает. Тем не менее, это идиома, чья полезность в значительной степени прошла - силы, которые ее мотивировали (медленная незащищенная синхронизация, медленный запуск JVM), больше не играют в игру, что делает ее менее эффективной как оптимизация. Ленькая идиома держателя инициализации предлагает те же преимущества и легче понять.
Ответ 2
Да, экземпляр должен быть изменчивым с помощью двойной проверки блокировки в Java, поскольку в противном случае инициализация MySingleton может выставить частично построенный объект для остальной части системы. Также верно, что потоки будут синхронизироваться, когда они достигнут "синхронизированного" оператора, но это слишком поздно в этом случае.
Wikipedia, а еще несколько вопросов о переполнении стека хорошо обсуждают "блокировку с двойной проверкой", поэтому я бы посоветовал прочитать об этом. Я бы также посоветовал не использовать его, если профилирование не показывает реальной потребности в производительности в этом конкретном коде.
Ответ 3
Там более безопасная и читаемая идиома для ленивой инициализации синглетов Java:
class Singleton {
private static class Holder {
static final Singleton instance = create();
}
static Singleton getInstance() {
return Holder.instance;
}
private Singleton create() {
⋮
}
private Singleton() { }
}
Если вы используете более подробный шаблон двойной проверки блокировки, вам нужно объявить поле volatile
, как уже отмечалось другим.
Ответ 4
См. http://www.javaworld.com/jw-02-2001/jw-0209-double.html
Ответ 5
Нет. Он не должен быть изменчивым.
См. Как разрешить "Блокировка с двойной проверкой" . Декларация на Java?
предыдущие попытки потерпели неудачу, потому что если вы можете обмануть java и избежать volatile/sync, java может обмануть вас и дать вам неправильный вид объекта. однако новая семантика final
решает проблему. если вы получите ссылку на объект (обычным чтением), вы можете спокойно прочитать его поля final
.