【发布时间】:2011-02-16 11:40:02
【问题描述】:
为什么实现 Runnable 比从 Thread 类扩展更好?
【问题讨论】:
标签: java
为什么实现 Runnable 比从 Thread 类扩展更好?
【问题讨论】:
标签: java
先回答问题:
如果你扩展线程,你的类的一个实例(扩展线程)将总是调用超类线程的构造函数。所以 我的班级 myclass= 新的我的班级() 将始终在实例化线程的 MyClass 的构造函数中调用“super()”,最后您可能会在您的类中实现一些开销(如果您不使用超类线程的任何方法)。所以只实现 Runnable 可以让你的类运行得更快,而没有任何继承开销。
此外,这里还有一些错误的答案:
现在我只扩展了少数清楚且明显通过“is-a”测试的东西,其他一切都是接口......
你没有考虑过创建一个没有任何接口的对象吗?因为为每个对象都实现一个接口是非常错误的!
当您扩展 Thread 类时,您的每个线程都会创建唯一的对象并与之关联。当您实现 Runnable 时,它会将同一个对象共享给多个线程。
错了,每次调用一个类的构造函数,都会得到一个自己的对象,不管是从哪里扩展,实现什么!
结论:在java中,每个类实例都是一个对象,并且扩展了类Object(而原语不是......只有像int []类这样的原语数组扩展了Object,等等数组具有与任何对象相同的方法 - 但遗憾的是,Javadoc 中没有提到它们!Sun 的人可能不希望我们使用它们)。
顺便说一句,继承至少有两个优点:代码共享和清晰的 Javadoc。因为如果你从一个类继承,你可以在 Javadoc 中看到所有的子类。您也可以使用界面来做到这一点,但是您不能共享代码。更重要的是,您将需要创建“嵌套”对象并调用两个或多个对象的构造函数,而不仅仅是一个,这再次意味着您从超类 Object 本身实现了一些开销!因为在 Java 中没有没有开销的对象,所以创建尽可能少的对象对于高性能应用程序非常重要。对象是最伟大的东西,但不必要的对象不是......
【讨论】:
您可能更喜欢实现Interface Runnable 而不是扩展Class Thread 的原因如下:
当您扩展 Thread 类时,您的每个线程都会创建唯一的 对象并与之关联。当您实现 Runnable 时,它共享 同一个对象到多个线程。
来源:a blog - 我不太确定这是否正确
【讨论】:
因为 IS-A 确实不是您想要的。你的类想要 Runnable,但是 IS-A Thread 感觉太强了。这就是继承的意思。您真的只想实现 run() 方法,而不是 Thread 类中的所有其他相关内容。
这符合 Scott Meyers 在“更有效的 C++”中的非常好的建议:使非叶类抽象。替换接口就可以了。
【讨论】:
要带走的实际要点是,在任何有问题的情况下,总是首选实现而不是扩展。
Extends 非常紧密地绑定了两个类文件,可能会导致一些非常难以处理的代码。
当我第一次“理解”面向对象编程时,我正在扩展一切,但它把我的整个设计变成了糊状。现在我只扩展了一些清楚而明显地通过“is-a”测试的东西,其他一切都是接口......
许多问题刚刚停止发生(令人困惑的多重继承情况,浪费时间重构层次结构,具有“受保护”变量的趋势然后想知道当您没有在当前类中更改它们时它们为什么会改变,对构造函数的链接要求,弄清楚不同的继承树如何相互交互,...
似乎每 3 年(过去 20 年)我认为我真的“开始”编程,并羞愧地回顾我 3 年前所做的愚蠢事情......这就是其中之一(但距离更近到 7 年前)
【讨论】:
在继承层次结构中扩展Thread 可能没有任何意义。如果您想修改Threads 的功能,请仅扩展Thread。
使用Runnable,任何继承层次结构中的任何类都可以公开一个任务,该任务可以被视为一个工作单元以让Thread 执行。
【讨论】:
这样你解耦计算(什么)与执行(何时和/或如何强>)。
使用Runnable 或Callable,例如,您可以将许多工作/计算提交给Executor,该Executor 将负责安排工作。这是ExecutorService的摘录形式:
pool = Executors.newFixedThreadPool(poolSize);
...
pool.execute(new Handler(serverSocket.accept()));
...
class Handler implements Runnable {
...
}
使用Runnable/Callable 比直接使用线程更灵活。
【讨论】: