Этот код решает проблему двойной проверки блокировки в Java? - PullRequest
5 голосов
/ 12 мая 2011

Этот код решает проблему двойной проверки блокировки в Java?

public class DBAccessService() {
    private static DBAccessService INSTANCE;  

    private DBAccessService() {}

    public static DBAccessService getInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        return createInstance();
    }

    private static synchronized DBAccessService createInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        DBAccessService instance = new DBAccessService();
        INSTANCE = instance;

        return INSTANCE;
    }
}

Есть 2 аспекта, на которые следует обратить внимание:

  1. getInstance() является не синхронизированным, поэтому после инициализации INSTANCE стоимость синхронизации не увеличивается
  2. createInstance() синхронизируется

Итак, вопрос: есть ли у этого кода какие-либо проблемы? Это законно и всегда потокобезопасно?

Ответы [ 5 ]

7 голосов
/ 12 мая 2011

Вам нужно объявить INSTANCE как volatile, чтобы он работал:

private static volatile DBAccessService INSTANCE;

Обратите внимание, что он работает только с Java 5 и более поздними версиями.См. Декларация «Двойная проверка заблокирована» .

4 голосов
/ 12 мая 2011

Для решения этого конкретного вопроса Параллелизм Java на практике (написанный командой, которая в основном написала библиотеку java.util.concurrent) рекомендует Идиома класса владельца отложенной инициализации (стр. 348) в моем экземпляре, листинг 16.6, а не 16.7)

@ThreadSafe
public class DBAccessServiceFactory {
  private static class ResourceHolder {
    public static DBAccessService INSTANCE = new DBAccessService();
  }
  public static DBAccessService getResource() {
    return ResourceHolder.INSTANCE;
  }
}

Это всегда законно и потокобезопасно. Я не эксперт, поэтому не могу сказать, что это лучше, чем ваш код. Однако, учитывая, что это шаблон, рекомендованный Дагом Ли и Джошуа Блохом, я всегда буду использовать его над кодом, который вы или я изобрели, так как на нем очень легко ошибаться (о чем свидетельствует количество неправильных ответов на этот вопрос). ).

В связи с нестабильной проблемой они говорят:

Последующие изменения в JMM (Java 5.0 и более поздние версии) позволили DCL работать, если ресурс стал нестабильным ... Однако идиома ленивого держателя инициализации предлагает те же преимущества и проще для понимания.

2 голосов
/ 12 мая 2011

В этой статье утверждается, что "дважды проверенная регистрация" не является проблемой, если вы используете отдельный класс Singleton:

public class DBAccessHelperSingleton {
    public static DBAccessHelper instance = new DBAccessHelper(); 
} 

Оно имеет то же преимущество: поле не создается, пока не будет впервые использовано для ссылок.

Как указывалось ранее, если вы хотите сохранить, как у вас есть volatile отсутствует, если вы только нацеливаетесь на JDK> = 5.

1 голос
/ 12 мая 2011

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

public class DBAccessService() {
     public static DBAccessService INSTANCE = new DBAccessService();
}
0 голосов
/ 12 мая 2011

выглядит хорошо.Если два потока вызывают getInstance () и INSTANCE не инициализированы, только один поток сможет продолжить с createInstance (), а второй уже увидит, что экземпляр не нулевой.

Единственное, что является volatile ключевое слово для INSTANCE.В противном случае Java может его кешировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...