Вот потокобезопасный singleton
:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Никак не пойму зачем ему volatile
, если он и так лочится по Singleton.class
. От чего это спасает ?
Операция создания объекта в Java не является атомарной.
Рассмотрим (один из возможных) пример с выполнением операции создания объекта и двумя потоками:
Поток A
входит в метод getInstance()
, в этот момент времени instance == null
и он входит в синхронизированный блок, в котором тоже instance == null
и начинает создание объекта Singleton
. Сначала выделяется память под объект, потом этот объект инициализируется ссылкой на выделенную область памяти. В этот момент времени Поток B
заходит в метод getInstance()
видит, что instance != null
и начинает использовать уже существующий, но еще не донца сконструированный объект (так как его поля еще не инициализированы).
Объявление поля instance
как volatile
(JDK 5+) устанавливает отношение happens before между инициализацией объекта instance
Потоком A
и возвратом объекта instance
Потоку B
.
Иными словами, объявление поля instance
как volatile
гарантирует, что поток В
прочитает уже полностью сконструированный объект instance
.
UPD. Из комментариев @Roman еще одна причина необходимости использования volatile
:
Без volatile
есть ещё одна проблема: если первая проверка if (instance == null)
увидит не null
, последующий return instance
может увидеть null
, в результате метод вернёт null
.
Если нет корректной синхронизации, то чтение, которое идёт "позже" может увидеть значение, которое было "раньше", т.к. JMM не запрещает переупорядочивать такие чтения.
Переменная, объявленная volatile
, никогда не кешируется в память потока, то есть она в любой момент времени в любом потоке будет иметь одинаковое (актуальное) значение (если один поток меняет ее значение, то это значение сразу же доступно в других потоках).
Потому что чтение при сравнении
if (instance == null) {
происходит вне synchronized блока, а так уже и sideeffects могут быть, например, созданный объект в heap, но с ещё не вызванным конструктором или кешированное значение null. Модель памяти JDK 5.0 и выше требует этого, чтобы поведение многопоточных программ было предсказуемым.
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
Нужна помощь в структуризации моих знаний о том как выводится графики и проблема эта появилась в момент когда я писал простенькую игрушку...
Подскажите, как можно загрузить изображение не в ImageView а в папку с ресурсами либо установить загруженное изображение сразу в качестве фона...
При попытке вызвать paintComponent с передачей аргументов происходит ошибка, если я удаляю аргументы и назначаю внутри paintComponent свои все работает