2013-03-02 4 views
3

Типичный ленивый синглтон:Почему мы синхронизируем ленивые одиночные игры, но не нетерпеливы?

public class Singleton { 
    private static Singleton INSTANCE; 

    private Singleton() { 

    } 

    public static synchronized Singleton getInstace() { 
     if(INSTANCE == null) 
      INSTANCE = new Singleton(); 

     return INSTANCE; 
    } 
} 

Типичные нетерпеливый синглтон:

public class Singleton { 
    private static Singleton INSTANCE = new Singleton(); 

    public static Singleton getInstance() { 
     return INSTANCE; 
    } 
} 

Почему мы не обеспокоены синхронизациями с жаждущими одиночками, но придется беспокоиться о синхронизации с их ленивыми кузинами?

+1

Потому что нет возможных условий гонки? –

+2

@ OliCharlesworth - Но ленивые одиночки не участвуют в гонке, они просто шарят. –

+0

Спасибо @OliCharlesworth (+1) - можете ли вы объяснить с более подробным ответом? – IAmYourFaja

ответ

0

Поскольку инициативный синглтон инициализируется при первом запуске класса (jit), и это происходит только один раз. Однако, если два клиента попытаются одновременно вызвать метод экземпляра singleton из двух потоков, могут быть созданы два синглтона.

+0

Как создаются два синглтона? –

+0

Если не синхронизировано, оба потока могут достигать нулевой проверки, см., Что это правда, и создать новый экземпляр. –

+0

. Как статические могут иметь более одного значения? –

0

Потому что в последнем примере экземпляр Singleton всегда присутствует, когда вызывается getInstance - здесь ничего не синхронизируется. Это противоречит первому примеру, когда экземпляр еще не инициализирован. В этом случае getInstance содержит критический раздел (if и его тело), ​​который необходимо защитить (например, синхронизацией) от одновременного доступа.

+0

Хотя это вызывает интересный момент (по крайней мере, в моей голове!); что произойдет, если статический инициализатор в другом классе решит вызвать 'Singleton.getInstance()'? –

+0

@OliCharlesworth, поэтому ключевое слово 'synchronized' присутствует в подписи' getInstance' – SomeWittyUsername

+0

Я имею в виду в нетерпеливой версии. (Мой вопрос не имеет ничего общего с параллелизмом, я просто понял, что не знаю, как Java определил относительный порядок статических инициализаций разных классов.) –

0

Eager-экземпляр не требует явной синхронизации для обмена ссылкой поля, потому что JVM обработает его для нас уже как часть механизма загрузки класса.

Чтобы более подробно изучить, до того, как класс станет доступен для любого используемого потока, он будет загружен, проверен и инициализирован. Компилятор перезаписывает статическое полевое назначение на этот этап инициализации и по правилам модели памяти Java, а основная аппаратная архитектура гарантирует, что все потоки, которые обращаются к этому классу, увидят эту версию класса. Это означает, что JVM будет обрабатывать любые аппаратные барьеры и т. Д. Для нас.

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

private static **final** Singleton INSTANCE = new Singleton(); 

FYI Если вы заинтересованы, раздел 5.5 из Java Virtual Machine Specification охватывает это в гораздо более подробно. Пара выбора фрагментов из спецификации является

*"Because the Java Virtual Machine is multithreaded, 
initialization of a class or interface requires careful 
synchronization"* 

*"For each class or interface C, there is a unique initialization lock LC"* 

*9&10) "Next, execute the class or interface initialization method of C" 
"If the execution of the class or interface initialization 
method completes normally, then acquire LC, label the Class object for 
C as fully initialized, notify all waiting threads, release LC, and 
complete this procedure normally."* 

Он находится в шаге 10 спецификации, где будут установлены статические поля, и использование блокировки (LC) используется для того, чтобы только один потока выполняет инициализацию и что результат распределяется правильно.

Смежные вопросы