java - java对象在构造过程中何时变为非空?

标签 java constructor null order-of-execution

假设您正在创建一个像这样的 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:

  • 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.
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.

然后从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/

相关文章:

c++动态二维数组不使用默认构造函数

c++ - 为什么我们在 C++ 中没有虚拟构造函数?

android - 如何从一项 Activity 中获取值(value)到另一项 Activity

javascript - 如果存在值(value),就这样做。未定义与空?

java - 处理算法中的问题/错误的推荐方法

java - 关闭 JFrame 窗口时如何使用 DefaultClosingOperation 以外的其他操作?

java - 新手 JLayeredPane 问题

java - 获取连接到 PHP 网站的用户的系统硬件信息?是否可以?

c++ - 当构造函数抛出异常时会运行哪些析构函数?

java - 如何以及何时检查哈希表中的键是否为空