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 中 覆盖 的方法。但这就像:不好的做法(因为它会导致这种奇怪的错误)。所以别想那么做。
假设我有以下 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 中 覆盖 的方法。但这就像:不好的做法(因为它会导致这种奇怪的错误)。所以别想那么做。