使用" private static"是否存在线程安全或可见性问题?字段而不是"私有最终静态"领域

时间:2014-05-30 06:08:23

标签: java thread-safety singleton visibility

关于静态初始化程序是线程安全的并且在类加载时由单个线程执行,如果“私有静态字段”使我理解相同的线程安全,我为什么要在单例中使用“私有最终静态字段”?可见性保证 - 假设静态初始化后不会触及静态字段。

简单地说,我确实说下面的两个例子在多线程环境中表现相同:任何时候任何客户端都会返回相同的实例 - 甚至没有最微弱的差异 - 你同意吗?请以任何一种方式告诉我。

示例静态字段:

public class Singleton {

    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }

    private Singleton() {
    }
}

最终静态字段示例:

public class Singleton_final {

    private final static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }

    private Singleton() {
    }
}

亲切的问候, 赫尔曼

2 个答案:

答案 0 :(得分:1)

如果是您提供的代码,则没有任何区别,因为您没有实际设置Singleton课程新实例的方法,因为您没有在课堂外可见的构造函数或setter。

但是,如果没有final可以更改Singleton实例,则无法说明。如果您没有final限定符,理论上可以使用Reflection API来更改实例。我在理论上说,因为我自己从来没有尝试过,而且我无法想到一个偏离脑海的方法。我对任何对Reflection API有更多经验的人都感兴趣

答案 1 :(得分:1)

与许多多线程问题一样,它有点微妙。请允许我稍微深入研究一下这个问题,然后提供一个有点答案。

提炼问题

如果客户看到非null引用,他们将只看到您创建的Singleton。其中毫无疑问。以下是两个问题:

  • 任何人都可以看到null
  • 是否有人可以看到部分构建的Singleton

后一个问题与您的案例不太相关,因为Singleton没有任何州。但如果它确实有状态,并且该状态存储在非final字段中,那么这将是一个问题。例如,给定:

public class Singleton {
    private /* non-final */ String name;

    private Singleton(String name) {
        this.name = name;
    }
}

...部分构造的Singleton是名称为null的{​​{1}}(尽管在施工时已设置为非-null值。)

你想要的可能是(a)没有人看到null和(b)没有人看到部分构造的对象。要实现这一点,您需要该类没有数据竞争。要做那个,你需要一个先发生过的关系。

所以问题是:初始化instance的线程与之后读取它的线程之间是否存在先发生关系?

那么,答案是什么?

JLS对此有点不确定。在JLS 12.4.2有一个类初始化的详细描述,其中包括锁定类,从而引入一个先发生的边缘。但是JLS中没有任何内容可以指定当一个类已经初始化时会发生什么!在这些情况下,JLS不需要任何锁定,因此不会建立任何先发生关系。严格阅读JLS会表明其他线程中的客户端可能看到null引用或部分构造的对象。

JLS暗示这不应该发生in 12.4.1

  

意图是类或接口类型具有一组初始化器,使其处于一致状态,并且该状态是其他类观察到的第一个状态。

嗯,这很好,这是“意图”,但没有任何东西(除了意图声明)要求它。

final字段(静态或非静态)获取特殊线程安全语义(JLS 17.5,或在SO上看this question),它基本上提供前面提到的发生前边缘,因此删除数据竞争并确保对完全构造的对象进行非null引用。