协慌网

登录 贡献 社区

Java 中的 “实现 Runnable” 与“扩展线程”

从我在 Java 中使用线程的时间开始,我发现了这两种编写线程的方法:

使用implements Runnable

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

或者,使用extends Thread

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

这两个代码块有什么显着差异吗?

答案

是的:实现Runnable是首选方法,IMO。你并不是真正专注于线程的行为。你只是给它一些东西来运行。这意味着构图哲学上 “更纯粹” 的方式。

实际上 ,它意味着您可以实现Runnable并从另一个类扩展。

tl; dr:实现 Runnable 更好。但是,警告很重要

一般来说,我建议使用像Runnable而不是Thread这样的东西,因为它允许你保持你的工作只与你选择的并发性松散耦合。例如,如果你使用Runnable并稍后决定这实际上不需要它自己的Thread ,你可以调用 threadA.run()。

警告:在这里,我强烈反对使用原始线程。我更喜欢使用CallablesFutureTasks (来自 javadoc:“可取消的异步计算”)。超时,正确取消和现代并发支持的线程池的集成对我来说比成堆的原始线程更有用。

后续:有一个FutureTask构造函数 ,允许您使用 Runnables(如果这是您最熟悉的),并且仍然可以获得现代并发工具的好处。引用 javadoc:

如果您不需要特定结果,请考虑使用以下形式的结构:

Future<?> f = new FutureTask<Object>(runnable, null)

因此,如果我们用threadA替换他们的runnable ,我们得到以下内容:

new FutureTask<Object>(threadA, null)

允许您更接近 Runnables 的另一个选项是ThreadPoolExecutor 。您可以使用execute方法传入 Runnable 以在将来某个时间执行 “给定任务”。

如果您想尝试使用线程池,上面的代码片段将变为如下所示(使用Executors.newCachedThreadPool()工厂方法):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
1
88250
贡献值 86
贡献次数 1

据说:

仅在你要覆盖某些行为时使用继承。

更确切地说,可以理解为:

少用继承,多用接口。