假设我有一个实现Runnable接口的抽象Base类。
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创建的派生线程 Derivative正在运行param 1
但是在JAVA中启动一个在构造函数中调用抽象方法的线程是否安全?因为在C ++和C#中,在大多数情况下它是不安全的,据我所知。 谢谢!
答案 0 :(得分:30)
此代码演示了为什么从不从构造函数中调用抽象方法或任何其他可覆盖方法的原因:
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
完成初始化之前获得优先级并执行。
答案 1 :(得分:3)
不是一个好主意,因为在调用run()时,Derivative对象可能尚未初始化。如果run()依赖于Derivative中的任何状态,它可能会失败。
在你的简单案例中,它有效。但是那个子类没有意义。你可以简单地
public Base(final int param, Runnable action) {
new Thread(action).start();
答案 2 :(得分:2)
从构造函数中调用抽象方法是一种非常糟糕的做法。从构造函数调用的方法应该始终是私有的或最终的,以防止覆盖。
查看问题here
的链接答案 3 :(得分:1)
将this
从构造函数中传出称为“让this
从构造函数中逃脱”,并且可能导致一些特别讨厌和奇怪的错误,因为该对象可能处于不一致状态。 / p>
当this
传递给另一个线程时尤其如此,如本例所示。由于JVM有权对线程内的语句进行重新排序,因此可能会出现未定义的行为/状态。