Это безопасная версия блокировки с двойной проверкой? - PullRequest
1 голос
/ 13 января 2010

Вот идея, которую я только что предложил, для безопасного и эффективного способа решения проблем синхронизации Singleton.Это в основном двойная проверка блокировки, но с поворотом, который включает локальное хранение потока.В псевдокоде в стиле Java / C # / D предполагается, что __thread обозначает локальное хранилище потока для статических переменных:

class MySingleton {
    __thread static bool isInitialized; 
    static MySingleton instance; 

    static MySingleton getInstance() {
        if(!isInitialized) {
            synchronized {
                isInitialized = true;
                if(instance is null) {
                    instance = new MySingleton();
                }
            }
        }

        return instance;
   }
}

Это гарантирует, что блок synchronized будет входить только один раз на поток в течение всей жизнипрограммы.Начиная с этого момента, мы получаем простую проверку локального bool-потока, чтобы увидеть, вошли ли мы уже в синхронизированный блок и убедились, что объект инициализирован из этого потока.

Ответы [ 6 ]

4 голосов
/ 13 января 2010

Я не думаю, что независимый от языка (или не зависящий от платформы) подход здесь полезен, потому что, хотя конструкция может быть логически обоснованной, существуют специфические для реализации ошибки, которые не позволяют ей работать должным образом. Пример - блокировка с двойной проверкой, которая просто не работала на Java pre-5, потому что она была сломана на уровне JVM.

Таким образом, вы должны использовать доступные языковые конструкции или библиотеки на каждой платформе.

Для Java вы можете получить синглтоны, используя enum.

3 голосов
/ 13 января 2010

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

Поскольку isInitialized является локальным для потока, почему вы устанавливаете его внутри синхронизированного блока? Кроме того, вы должны устанавливать isInitalized только после создания вашего одноэлементного объекта. Таким образом, если он еще не был инициализирован и конструктор сгенерировал, этот поток проверит снова при следующем вызове.

    if(!isInitialized) {
        synchronized {
            if(instance is null) {
                instance = new MySingleton();
            }
        }
        isInitialized = true;
    }
2 голосов
/ 13 января 2010

Причиной того, что блокировка с двойной проверкой нарушена (насколько мне известно), является вероятность того, что instance не является нулевым, но и не полностью сконструирован из-за переупорядочения чтения / записи.хранение ничего не решит.Это может избавить вас от необходимости объявлять isInitialized изменчивым, но это все равно не решит вашу проблему.

1 голос
/ 14 января 2010

Да, эта конструкция безопасна на всех известных мне языках высокого уровня. В частности, это безопасно на любом языке, где модели памяти / параллелизма гарантируют, что данный поток всегда видит свои собственные операции в порядке, совместимом с порядком программы (что в значительной степени является любым полезным языком), и где синхронизированный блок или его эквивалент обеспечивает обычные гарантии в отношении операций до, внутри и после блока.

0 голосов
/ 13 января 2010

Да, при новой JMM jdk5, если вы объявите экземпляр volatile DCL будет работать

0 голосов
/ 13 января 2010

Выглядит хорошо для меня, если говорить о Java. Если вы собираетесь сделать это таким образом, более обычно сделать локальное хранилище потока ссылкой. Нет смысла читать статику.

Но в Java загрузка классов ленива и поточно-ориентирована, так что вы можете просто написать:

private static final MySingleton instance = new MySingleton(); 

Или не использовать синглтоны вообще.

...