private volatile static Singleton uniqueInstance
Почему в синглтоне при использовании метода двойной блокировки для синхронизации один экземпляр объявляется как volatile? Могу ли я добиться той же функциональности, не объявляя ее изменчивой?
private volatile static Singleton uniqueInstance
Почему в синглтоне при использовании метода двойной блокировки для синхронизации один экземпляр объявляется как volatile? Могу ли я добиться той же функциональности, не объявляя ее изменчивой?
Без volatile
код работает неправильно с несколькими потоками.
Из блокировки с двойной проверкой Википедии:
Начиная с J2SE 5.0, эта проблема устранена. Ключевое слово volatile теперь гарантирует, что несколько потоков правильно обработают одноэлементный экземпляр. Эта новая идиома описана в декларации "Блокировка с двойной проверкой нарушена". а>:
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result;
}
// other functions and members...
}
В общем, вам следует по возможности избегать блокировки с двойной проверкой, так как это трудно сделать правильно, а если вы ошиблись, может быть трудно найти ошибку. Вместо этого попробуйте этот более простой подход:
Если вспомогательный объект является статическим (по одному на загрузчик класса), альтернативой является идиома инициализации по запросу
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
synchronized
не гарантирует, что некэшированные значения полей будут извлечены, а это означает, что часть volatile
больше не нужна? Требуется ли это и сегодня?
- person android developer; 27.01.2021
volatile
предотвращает переупорядочивание операций записи в память, что делает невозможным чтение другими потоками неинициализированных полей вашего синглтона через указатель синглтона.
Рассмотрим такую ситуацию: поток A обнаруживает, что uniqueInstance == null
, блокируется, подтверждает, что он все еще null
, и вызывает конструктор синглтона. Конструктор выполняет запись в элемент XYZ
внутри Singleton и возвращает значение. Теперь поток A записывает ссылку на вновь созданный синглтон в uniqueInstance
и готовится снять блокировку.
Как только поток A готовится снять блокировку, появляется поток B и обнаруживает, что uniqueInstance
не является null
. Поток B
обращается к uniqueInstance.XYZ
, думая, что он был инициализирован, но поскольку ЦП переупорядочил записи, данные, которые поток A записал в XYZ
, не стали видимыми для потока B. Поэтому поток B видит внутри XYZ
неверное значение, т.е. неправильный.
Когда вы отмечаете uniqueInstance
volatile, вставляется барьер памяти. Все операции записи, инициированные до uniqueInstance
, будут завершены до того, как будет изменен uniqueInstance
, что предотвратит описанную выше ситуацию переупорядочивания.
uniqueInstance
и 2) XYZ
получает что-то значимое.
- person lcn; 15.06.2015
volatile
гарантирует, что A действительно записывается перед B. Без volatile
ЦП может свободно записывать B перед A , если это не оказывает заметного влияния на логику вашего кода.
- person Sergey Kalinichenko; 15.02.2020
, you assign a local
tmp = новый экземпляр(), then check again, and finally do
instance = tmp`. См. код Марка в принятом ответе.
- person Sergey Kalinichenko; 29.03.2021
Thread.MemoryBarrier();
перед назначением переменной?
- person Bogdan Mart; 09.04.2021
Чтобы избежать использования двойной блокировки или нестабильности, я использую следующее
enum Singleton {
INSTANCE;
}
Создание экземпляра простое, ленивая загрузка и потокобезопасность.
Запись в изменчивое поле произойдет до любой операции чтения. Ниже приведен пример кода для лучшего понимания:
private static volatile ResourceService resourceInstance;
//lazy Initialiaztion
public static ResourceService getInstance () {
if (resourceInstance == null) { // first check
synchronized(ResourceService.class) {
if (resourceInstance == null) { // double check
// creating instance of ResourceService for only one time
resourceInstance = new ResourceService ();
}
}
}
return resourceInstance;
}
Эта ссылка может помочь вам лучше http://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html
Вы можете использовать следующий код:
private static Singleton uniqueInstance;
public static synchronized Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance
}