允许此引用转义

IUn*_*own 48 java concurrency

我非常感谢帮助理解"Java Concurrency in Practice"中的以下内容:

从构造函数中调用可重写的实例方法(既不是私有也不是最终的)也可以允许此引用转义.

  1. 这里的'escape'是否意味着我们可能在实例完全构造之前调用实例方法?
    我没有看到'this'以任何其他方式逃避实例的范围.
  2. 'final'是如何防止这种情况发生的?在实例创建中是否存在"最终"的某些方面我缺少?

SLa*_*aks 27

  1. 它意味着在类之外调用代码并传递this.
    该代码将假定实例已完全初始化,如果不是,则可能会中断.
    类似地,您的类可能会假设某些方法仅在实例完全初始化后调用,但外部代码可能会破坏这些假设.

  2. final方法不能被覆盖,所以你可以相信它们不会传递this.
    如果final在构造函数中为非final类调用任何非方法,派生类可能会覆盖该方法并传递到this任何位置.
     
    即使你调用final方法,你仍然需要确保它们被安全地写入 - 它们不会传递到this任何地方,并且它们本身不会调用任何非final方法.


chr*_*ke- 22

"Escape"表示对部分构造的this对象的引用可能会传递给系统中的某个其他对象.考虑这种情况:

public Foo {
    public Foo() {
        setup();
    }

    protected void setup() {
       // do stuff
    }
}

public Bar extends Foo implements SomeListener {
    @Override protected void setup() {
        otherObject.addListener(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是新Bar对象otherObject在构建完成之前正在注册.现在,如果otherObject开始调用方法barObject,则字段可能尚未初始化,或者barObject可能处于不一致状态.在它准备好之前,对barObject(this自身)的引用已经"逃逸"到系统的其余部分.

相反,如果该setup()方法final处于启用状态Foo,则Bar该类不能在其中放置代码,以便在Foo构造函数完成之前使对象可见.


Sot*_*lis 12

我相信这个例子是这样的

public class Foo {
    public Foo() {
        doSomething();
    }

    public void doSomething() {
        System.out.println("do something acceptable");
    }
}

public class Bar extends Foo {
    public void doSomething() {
        System.out.println("yolo");
        Zoom zoom = new Zoom(this); // at this point 'this' might not be fully initialized
    }
}
Run Code Online (Sandbox Code Playgroud)

因为总是首先调用超级构造函数(隐式或显式),所以doSomething将始终为子类调用.因为上面的方法既不是也不finalprivate,你可以在子类中覆盖它并做任何你想做的事情,这可能与我们想做的事情相冲突Foo#doSomething().


Woo*_*Moo 5

安全编码

示例 BAD代码:

final class Publisher {
  public static volatile Publisher published;
  int num;

  Publisher(int number) {
    published = this;
    // Initialization
    this.num = number;
    // ...
  }
}   
Run Code Online (Sandbox Code Playgroud)

如果对象的初始化(以及它的构造)依赖于构造函数中的安全性检查,则当不受信任的调用者获得部分初始化的实例时,可以绕过安全性检查.见规则OBJ11-J.警惕让构造函数抛出异常以获取更多信息.

final class Publisher {
  public static Publisher published;
  int num;

  Publisher(int number) {
    // Initialization
    this.num = number;
    // ...
    published = this;
  }
}
Run Code Online (Sandbox Code Playgroud)

因为该字段是非易失性和非最终字段,所以构造函数中的语句可以由编译器重新排序,使得在执行初始化语句之前发布此引用.

正确的代码:

final class Publisher {
  static volatile Publisher published;
  int num;

  Publisher(int number) {
    // Initialization
    this.num = number;
    // ...
    published = this;
  }
}
Run Code Online (Sandbox Code Playgroud)

据说这个引用在超出当前范围时可以转义.以下是此引用可以转义的常用方法:

Returning this from a non-private, overridable method that is invoked from the constructor of a class whose object is being
Run Code Online (Sandbox Code Playgroud)

建造.(有关更多信息,请参阅规则MET05-J.确保构造函数不调用可覆盖的方法.)从可变类的非私有方法返回此方法,该方法允许调用方间接操作对象的状态.这通常发生在方法链实现中; 见规则VNA04-J.确保对链式方法的调用是原子的,以获取更多信息.将此作为参数传递给从正在构造对象的类的构造函数调用的外来方法.使用内部类.除非内部类声明为static,否则内部类隐式地保存对其外部类的实例的引用.通过将其分配给正在构造其对象的类的构造函数的公共静态变量来进行发布.从构造函数中抛出异常.这样做可能会导致代码容易受到终结器攻击; 见规则OBJ11-J.警惕让构造函数抛出异常以获取更多信息.将内部对象状态传递给外来方法.这使该方法能够检索内部成员对象的此引用.

此规则描述了在对象构造期间允许此引用转义的潜在后果,包括竞争条件和不正确的初始化.例如,通常声明一个字段最终确保所有线程都看到该字段处于完全初始化状态; 但是,在对象构造期间允许此引用转义可以将字段暴露给处于未初始化或部分初始化状态的其他线程.规则TSM03-J.不要发布部分初始化对象,它描述了各种安全发布机制所提供的保证,依赖于遵守此规则.因此,程序在对象构造期间不得允许此引用转义.

通常,检测此引用可能超出当前上下文范围的情况非常重要.特别是,应仔细审查公共变量和方法.