Java 并发 - 内联初始化的非最终字段和安全发布

Java concurrency - inline initialized non-final field and safe publication

假设我有以下 class:

public abstract class Test {
    private List<String> strings = new ArrayList<>();

    // to be called concurrently
    public int getStringsSize() {
        return strings.size();
    }
}

getStringsSize() 是否有可能为 Test 的某些子 class 抛出 NPE?或者还有什么可能出错?据我了解,对于这种情况没有 Java 内存模型保证。

没有。只要 strings 字段没有被重新分配(子类永远不会这样做,因为它是私有的),那么它永远不会在空状态下被访问,无论有多少线程试图访问它。因此,strings.size() 永远不会抛出 NullPointerException.

如果您手动将 strings 设置为 null

getStringSize() 只会抛出 NPE,EG 在 setStrings() 方法中。 getStringSize()也应该设置为final.

初始化程序在实例化时运行,即在 super(...) 构造函数之后,但在 this(...) 构造函数的第一条指令之前。

重点是:当一个subclass对象被实例化时,super构造函数和super field init语句将在before 执行任何子class 代码。有关详细信息,请参阅 here or there。换句话说:在通过 new 创建子 class 的 client 访问新创建的对象之前,可以保证执行这些 init 语句。

并且当我们谈论仅存在于超级 class 中的单个 private 字段时,该方法调用没有机会产生 NPE

除非您有其他方法将字段重置为空。您可以通过对该字段使用 final 关键字来避免这种情况(无论如何这是一个好习惯:将 default 设为 final 在字段上)。

终于;为了完整性:可以编写 super/subclass 代码,但由于 "something not initialized yet" 在 subclass 上执行 new 时会失败 - 就像当您的超级 class 构造函数调用时在子 class 中 覆盖 的方法。但这就像:不好的做法(因为它会导致这种奇怪的错误)。所以别想那么做。