初始化按需持有者成语线程安全,没有最终修饰符 [英] Is Initialization On Demand Holder idiom thread safe without a final modifier

查看:201
本文介绍了初始化按需持有者成语线程安全,没有最终修饰符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一种预感,即使用持有者惯用语而不将持有者字段声明为final是不是线程安全的(由于Java中的不变性方式)。有人可以证实这一点(希望有一些消息来源)吗?

I have a hunch that using the holder idiom without declaring the holder field as final is not thread safe (due to the way immutability works in Java). Can somebody confirm this (hopefully with some sources)?

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;
    }

}

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

I definitely want sourced statements, not just assertions like "it works" -- please explain/prove it's safe

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

A little modification to make my point more clear - can I be sure that the getAnswer() method will return 21 regardless of calling thread?

推荐答案

类初始化过程保证如果使用静态初始化程序设置静态字段的值(即 static variable = someValue; )该值对所有线程都可见:

The class initialization procedure guarantees that if a static field's value is set using a static initializer (i.e. static variable = someValue;) that value is visible to all threads:


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

10 - If the execution of the initializers 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.






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


Regarding your edit, let's imagine a situation with two threads T1 and T2, executing in that order from a wall clock's perspective:


  • T1: Something s = Something.getInstance();

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

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

然后你有:


  • T1获取LC,T1运行 Something INSTANCE = new Something(); ,初始化回答,T1释放LC

  • T2尝试获取LC,但已经被T1 =>等待锁定。当T1发布LC时,T2获取LC,读取 INSTANCE 然后读取 answer

  • T1 acquire LC, T1 run Something INSTANCE = new Something();, which initialises answer, T1 release LC
  • T2 tries to acquire LC, but already locked by T1 => waits. When T1 releases LC, T2 acquire LC, reads INSTANCE then reads answer.

所以你可以看到你在写入和读取回答之间有一个合适的事先关系,谢谢到 LC 锁定。

So you can see that you have a proper happens-before relationship between the write and the read to answer, thanks to the LC lock.

这篇关于初始化按需持有者成语线程安全,没有最终修饰符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆