Блокировка с двойной проверкой против синглтона

Java Шаблоны проектирования

Блокировка с двойной проверкой и синглтоны — ссылка на исходный текст
Блокировка с двойной проверкой и синглтоны — ссылка на исходный текст
Блокировка с двойной проверкой и синглтоны — ссылка на исходный текст

Я считаю, что для шаблона синглтона большинство людей могут написать несколько методов реализации, таких как ленивые люди, голодные люди и т. д. Однако хорошо написать маленькие синглтоны непросто, и не просто написать их полностью.

Синглтон для замков с двойной проверкой

Ниже приведена реализация синглтона, которую мы часто используем, то есть реализация двойной проверки.

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        
    }

    public Singleton getInstance() {
        if (null == instance) {
            synchronized (Singleton.class) {
                if (null == instance) {
                    instance = new Singleton();   // error
                }
            }
        }
        return uniqueSingleton;
    }
}  

Давайте посмотрим, как работает этот код: во-первых, когда поток делает запрос, он сначала проверяет, является ли экземпляр нулевым, и если нет, возвращает его содержимое напрямую, избегая тем самым ресурсов, необходимых для входа в синхронизированный блок. Во-вторых, если два потока входят в первое if-суждение одновременно, то они также должны выполнять код в синхронизированном блоке по порядку.Первый поток, входящий в блок кода, создаст новый экземпляр Singleton, а последующие потоки будут выполняться, потому что of Невозможно судить по if без создания избыточных экземпляров.

Но все же есть проблема, в некоторых случаях полученный таким образом объект Singleton может быть неправильным.

Просмотрите 3 шага нашего нового объекта

  • 1. Выделить место в памяти

  • 2. Инициализировать объект

  • 3. Укажите объект на только что выделенное пространство памяти

Однако, когда jvm оптимизирует инструкции, шаги 2 и 3 будут обратными. Например, после того, как поток 1 признан нулевым на двух уровнях, он переходит к новому действию. Если объект не был инициализирован, значение адреса возвращается, и поток 2. В первом нулевом решении, поскольку объект не пустой, объект возвращается напрямую. Однако когда поток 2 намеревается использовать экземпляр Singleton, он обнаруживает, что он не был инициализирован, и возникает ошибка.

решение

Для вышеуказанной проблемы есть два решения

1. Использование ключевого слова volatile в основном гарантирует, что порядок выполнения кода не повлияет на переупорядочивание jvm.

public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {
    }

    public Singleton getInstance() {
        if (null == instance) {
            synchronized (Singleton.class) {
                if (null == instance) {
                    instance = new Singleton();   // error
                }
            }
        }
        return instance;
    }
}

2. Реализовать одноэлементный шаблон в многопоточной среде с помощью внутренних классов.

public class Singleton {        
    
    private Singleton() {       
    }        
    
    private static class SingletonContainer {        
        private static Singleton instance = new Singleton();        
    }        
    
    public static Singleton getInstance() {        
        return SingletonContainer.instance;        
    }        
} 

Подписывайтесь на нас

关注我们