26

假设我有一个实现 Runnable 接口的抽象基类。

public abstract class Base implements Runnable {

  protected int param;

  public Base(final int param) {
      System.out.println("Base constructor");
      this.param = param;
      // I'm using this param here
      new Thread(this).start();
      System.out.println("Derivative thread created with param " + param);
  }

  @Override
  abstract public void run();
}

这是少数派生类之一。

public class Derivative extends Base {

  public Derivative(final int param) {
      super(param);
  }

  @Override
  public void run() {
      System.out.println("Derivative is running with param " + param);
  }

  public static void main(String[] args) {
      Derivative thread = new Derivative(1);
  }

}

关键是我希望我的 Base 类做一些一般的事情,而不是每次都复制它。实际上,它运行良好,输出始终相同:

基础构造函数 使用参数 1 创建的衍生线程 衍生线程使用参数 1 运行

但是在 JAVA 中启动一个线程调用构造函数中的抽象方法是否安全?因为,据我所知,在大多数情况下,在 C++ 和 C# 中它是不安全的。谢谢!

4

4 回答 4

37

这段代码演示了为什么你不应该构造函数中调用抽象方法或任何其他可覆盖的方法:

abstract class Super {
    Super() {
        doSubStuff();
    }
    abstract void doSubStuff();
}

class Sub extends Super {
    String s = "Hello world";

    void doSubStuff() {
        System.out.println(s);
    }
}

public static void main(String[] args) {
    new Sub();
}

运行时,将打印null. 这意味着构造函数中唯一的“安全”方法是私有和/或最终方法。

另一方面,您的代码实际上并没有从构造函数中调用抽象方法。相反,您将未初始化的对象传递给另一个线程进行处理,这更糟糕,因为您正在启动的线程可能被赋予优先级并在您Base完成初始化之前执行。

于 2013-03-10T21:07:57.770 回答
3

这不是一个好主意,因为在调用 run() 时,可能尚未初始化 Derivative 对象。如果 run() 依赖于 Derivative 中的任何状态,它可能会失败。

在您的简单情况下,它可以工作。但是子类没有意义。你可以简单地

public Base(final int param, Runnable action) {

  new Thread(action).start();
于 2013-03-10T20:59:31.500 回答
2

从构造函数调用抽象方法是一种非常糟糕的做法。从构造函数调用的方法应该始终是私有的或最终的,以防止被覆盖。

在此处查看此问题的链接

于 2013-03-10T20:59:22.793 回答
1

传递this出构造函数被称为“让this构造函数逃脱”,并且会导致一些特别讨厌和奇怪的错误,因为对象可能处于不一致的状态。

this传递给另一个线程时尤其如此,如本例所示。由于 JVM 有权在线程中重新排序语句,您可能会发生未定义的行为/状态。

于 2013-03-10T23:18:58.563 回答