2014-01-08 4 views
6

У меня есть догадка, что использование именования держателя без объявления поля держателя как окончательного не является потокобезопасным (из-за того, что неизменяемость работает на Java). Может ли кто-нибудь подтвердить это (надеюсь, с некоторыми источниками)?Инициализация по требованию Держатель идиомы потокобезопасен без окончательного модификатора

public class Something { 
    private long answer = 1; 

    private Something() { 
     answer += 10; 
     answer += 10; 
    } 

    public int getAnswer() { 
     return answer; 
    } 

    private static class LazyHolder { 
     // notice no final 
     private static Something INSTANCE = new Something(); 
    } 

    public static Something getInstance() { 
     return LazyHolder.INSTANCE; 
    } 

} 

EDIT: Я определенно хочу Sourced заявления, а не только утверждения, как «работает» - пожалуйста, объясните/доказать, что это безопасно

EDIT2: Немного изменений, чтобы сделать мой пункт более ясно - я могу быть что метод getAnswer() вернет 21 независимо от вызывающего потока?

+1

Это совершенно нитевидный, окончательный или нет. – Bohemian

+0

Источники для этого утверждения? –

+0

Модель памяти Java - я отвечу ... oops, слишком поздно: @assylias сделала это для меня. – Bohemian

ответ

15

The class initialization procedure гарантирует, что если значение статического поля имеет значение с помощью статического инициализатора (т.е. static variable = someValue;), что значение является видимым для всех потоков:

10 - Если исполнение инициализаторов завершается нормально, то получить LC, пометить объект класса C как полностью инициализированный, уведомить все ожидающие потоки, высвободить LC и выполнить эту процедуру в обычном режиме.


Что касается вашего редактирования, давайте представим ситуацию с двумя потоками Т1 и Т2, выполняется в таком порядке с точки зрения настенных часов в:

  • T1: Something s = Something.getInstance();
  • T2: Something s = Something.getInstance(); i = s.getAnswer();

У вас есть:

  • Т1 приобретают ЛК, Т1 запустить Something INSTANCE = new Something();, который инициализирует answer, Т1 выпуск LC
  • Т2 пытается получить LC, но уже заблокирован T1 => ждет. Когда T1 освобождает LC, T2 получает LC, читает INSTANCE, затем читает answer.

Таким образом, вы можете видеть, что у вас есть собственные происходят, прежде чем отношения между записью и чтением в answer, благодаря LC замку.

+0

@edit - мне было ясно после того, как вы ответили, но спасибо за пример в любом случае –

+0

@assylias, я не уверен, возможен ли здесь сценарий, когда '' '' '' '' '' '(ссылка) публикуется, но 'answer' не инициализируется все же, или я ошибаюсь? Благодарю. –

2

Это поточно-безопасный, но изменчивый. Так что любой, кто получает его, может назначить его другому. И это в первую очередь беспокоиться (даже перед тем, как учитывать безопасность потоков).

+5

Только никто, кроме класса 'Something' *, не может его получить. –

+0

Да, это правда +1 от меня. Таким образом, проблема мутации может возникать только в методах класса Something. Но я думаю, что это потокобезопасно из-за того, что загрузка классов синхронизирована в любом случае (независимо от того, окончательный или окончательный). –

+0

@MarkoTopolnik - конечно, кто-нибудь может использовать общедоступный метод, чтобы получить экземпляр, что вы имеете в виду? –

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