初始化按需持有者惯用线程安全,没有最终修饰符

时间:2014-01-08 11:59:53

标签: java multithreading singleton immutability idioms

我有一种预感,即使用holder noti而不将holder字段声明为final是不是线程安全的(由于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;
    }

}

编辑:我绝对想要源语句,而不仅仅是“它有效”这样的断言 - 请解释/证明它是安全的

EDIT2:稍加修改以使我的观点更清楚 - 无论调用线程如何,我都能确定getAnswer()方法将返回21吗?

2 个答案:

答案 0 :(得分:15)

class initialization procedure保证如果使用静态初始值设定项(即static variable = someValue;)设置静态字段的值,则该值对所有线程都可见:

  

10 - 如果初始化程序的执行正常完成,则 获取LC,将C的Class对象标记为完全初始化,通知所有等待的线程,释放LC ,并正常完成此程序。


关于你的编辑,让我们想象一下两个线程T1和T2的情况,从挂钟的角度按顺序执行:

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

然后你有:

  • T1获取LC,T1运行Something INSTANCE = new Something();,初始化answer,T1释放LC
  • T2尝试获取LC,但已被T1 =>锁定等待的时间。当T1释放LC时,T2获取LC,读取INSTANCE然后读取answer

由于answer锁定,您可以看到在写入和LC的读取之间存在正确的发生关系。

答案 1 :(得分:2)

肯定是线程安全的,但是可变的。因此,任何获得它的人都可以将其分配给其他人。这是首先要担心的事情(即使在考虑线程安全之前)。