假设您正在创建一个像这样的 java 对象:
SomeClass someObject = null;
someObject = new SomeClass();
什么时候 someObject 变为非空?是在 SomeClass()
构造函数运行之前还是之后?
为了澄清一点,假设另一个线程要检查 someObject
是否为 null 而 SomeClass()
构造函数已完成一半,它是 null 还是非-空?
此外,如果像这样创建 someObject
会有什么不同:
SomeClass someObject = new SomeClass();
someObject
会是 null 吗?
最佳答案
如果另一个线程“在”构造期间检查 someObject
变量,我相信它可能(由于内存模型中的怪癖)看到一个部分初始化的对象。新的(从 Java 5 开始)内存模型意味着任何 final 字段都应该在对象对其他线程可见之前设置为它们的值(只要对新创建的对象的引用不可见)以任何其他方式从构造函数中逃脱)但除此之外没有太多保证。
基本上,不要在没有适当锁定(或静态初始化器等提供的保证)的情况下共享数据 :) 说真的,内存模型非常棘手,就像一般的无锁编程一样。尽量避免这种情况成为可能。
在逻辑术语中,赋值发生在构造函数运行之后 - 因此如果您从同一线程观察变量,它将在运行期间为空构造函数调用。然而,正如我所说,内存模型存在一些奇怪之处。
编辑:出于双重检查锁定的目的,如果您的字段是 volatile
并且如果您'使用 Java 5 或更高版本。在 Java 5 之前,内存模型还不够强大。不过,您需要完全正确地获取模式。有关详细信息,请参阅 Effective Java,第 2 版,第 71 项。
编辑:这是我反对 Aaron 的内联在单个线程中可见的理由。假设我们有:
public class FooHolder
{
public static Foo f = null;
public static void main(String[] args)
{
f = new Foo();
System.out.println(f.fWasNull);
}
}
// Make this nested if you like, I don't believe it affects the reasoning
public class Foo
{
public boolean fWasNull;
public Foo()
{
fWasNull = FooHolder.f == null;
}
}
我相信这将总是报告true
。来自 section 15.26.1 :
Otherwise, three steps are required:
Otherwise, the value of the right-hand operand is converted to the type of the left-hand variable, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.
- First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
- Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
然后从section 17.4.5 :
两个 Action 可以通过 happens-before 关系排序。如果一个 Action 发生在另一个 Action 之前,那么第一个 Action 对第二个 Action 可见并排在第二个 Action 之前。
If we have two actions x and y, we write hb(x, y) to indicate that x happens-before y.
- If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
- There is a happens-before edge from the end of a constructor of an object to the start of a finalizer (§12.6) for that object.
- If an action x synchronizes-with a following action y, then we also have hb(x, y).
- If hb(x, y) and hb(y, z), then hb(x, z).
It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. If the reordering produces results consistent with a legal execution, it is not illegal.
换句话说,即使在单个线程中也可以发生奇怪的事情,但这不能被观察到。在这种情况下,差异将是可以观察到的,这就是为什么我认为它是非法的。
关于java - java对象在构造过程中何时变为非空?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/681881/