java 线程在创建外部对象之前访问它

是的,这是一个学术问题,我知道人们会抱怨我没有发布任何代码,但我真的被这个问题击中了,真的不知道从哪里开始。我真的很感激一个解释,也许还有一些代码示例。

如果对象构造函数启动一个新线程来执行匿名内部类对象的方法运行,则此新线程可能会在完全构造该对象及其字段完全初始化之前访问其周围的外部对象。您将如何防止这种情况发生?


答案 1

这被称为“泄漏这个”。在这里你有代码

public class Test {

  // this is guaranteed to be initialized after the constructor
  private final int val;

  public Test(int v) {
    new Thread(new Runnable() {
      @Override public void run() {
        System.out.println("Val is " + val);
      }
    }).start();
    this.val = v;
  }

}

猜猜它会打印什么(可能,因为它是一个线程)。我使用了一个字段来强调对象在完全初始化之前被访问(最终字段必须在每个构造函数的最后一行之后明确分配final

如何恢复

当您处于构造函数中时,您不想传递。这也意味着您不希望在同一类(非静态,非私有)中调用非最终虚拟方法,并且不使用内部类(匿名类是内部类),这些类隐式链接到封闭实例,因此可以访问。thisthis


答案 2

首先考虑单线程情况:

每当您通过 创建对象时,都会调用其构造函数,该构造函数(希望)在返回对此对象的引用之前初始化新对象的字段。也就是说,从调用方的角度来看,这几乎就像一个原子操作:newnew

在调用之前,没有对象。从 返回后,对象存在完全初始化。newnew

所以一切都很好。


当多个线程发挥作用时,情况会略有变化。但我们必须仔细阅读您的报价:

...已完全构造,其字段已完全初始化。

关键点是.问题的主题行说“在创建之前”,但这里的意思不是在创建对象之前,而是在对象创建和初始化之间。在多线程情况下,由于这个原因,不能再被认为是(伪)原子的(时间从左到右流):fullynew

Thread1 --> create object --> initialize object --> return from `new`
                           ^
                           |
                           | (messing with the object)
Thread2 ------------------/

那么 Thread2 如何弄乱对象呢?它需要对该对象的引用,但由于仅在创建和初始化后才会返回该对象,因此这应该是不可能的,对吧?new

好吧,不 - 有一种方法仍然是可能的 - 即线程2是在对象的构造函数内创建的。然后情况会是这样的:

Thread1 --> create object --> create Thread2 --> initialize object --> return from `new`
                                      |       ^
                                      |       |
                                      |       | (messing with the object)
                                       \-----/

由于 Thread2 是在创建对象之后(但在对象完全初始化之前)创建的,因此已经有一个对 Thread2 可以获取的对象的引用。一种方法是,如果 Thread2 的构造函数显式地将对对象的引用作为参数。另一种方法是为 Thread2 的方法使用对象的非静态内部类。run