Я не понимаю, почему volatile используется. Разве volatile использование не противоречит цели использования блокировки с двойной проверкой, т. е. производительности?
Переведено автоматически
Ответ 1
Хороший источник для понимания того, почему volatile это необходимо, взят из книги JCIP . В Википедии также есть достойное объяснение этого материала.
Реальная проблема заключается в том, что Thread A может быть выделено пространство памяти для instance до завершения сборкиinstance. Thread B увидит это назначение и попытается его использовать. Это приводит к Thread B сбою, потому что используется частично сконструированная версия instance.
Ответ 2
Как указано @irreputable, volatile не является дорогостоящим. Даже если это дорого, согласованности следует уделять приоритетное внимание, а не производительности.
Есть еще один чистый элегантный способ для ленивых одиночек.
В программной инженерии идиома инициализации по требованию Holder (шаблон проектирования) представляет собой синглтон с отложенной загрузкой. Во всех версиях Java эта идиома обеспечивает безопасную, высококонкурентную отложенную инициализацию с хорошей производительностью
Поскольку в классе нет static переменных для инициализации, инициализация завершается тривиально.
Определение статического класса LazyHolder внутри него не инициализируется до тех пор, пока JVM не определит, что LazyHolder должен быть выполнен.
Статический класс LazyHolder выполняется только тогда, когда статический метод getInstance вызывается в классе Singleton , и в первый раз, когда это произойдет, JVM загрузит и инициализирует LazyHolder класс.
Это потокобезопасное решение, не требующее специальных языковых конструкций (т.Е. volatile Или synchronized).
Ответ 3
Ну, здесь нет блокировки с двойной проверкой производительности. Это сломанный шаблон.
Оставляя эмоции в стороне, volatile здесь потому, что без этого ко времени прохождения второго потока instance == null первый поток может еще не быть создан new Singleton(): никто не обещает, что создание объекта произойдет до присвоения instance любому потоку, кроме того, который фактически создает объект.
volatile в свою очередь устанавливает отношение "происходит до" между операциями чтения и записи и исправляет нарушенный шаблон.
Если вы ищете производительность, используйте вместо этого внутренний статический класс holder .
Ответ 4
Если бы у вас ее не было, второй поток мог попасть в синхронизированный блок после того, как первый установил для него значение null , и ваш локальный кэш по-прежнему думал бы, что это значение равно null.
Первый вопрос не для корректности (если бы вы были правы, это было бы обречено на провал), а скорее для оптимизации.