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

使用可运行的机具:

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

或者,使用扩展线程:

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

这两个代码块有什么显著的区别吗?


当前回答

实例化接口在代码和线程的实现之间提供了更清晰的分离,因此在这种情况下我更希望实现Runnable。

其他回答

如果我没有错的话,它或多或少类似于

接口和抽象类之间的区别是什么?

extends建立“Is A”关系,接口提供“Has A”功能。

首选工具可运行:

如果不需要扩展Thread类并修改Thread API默认实现如果你执行的是一个火灾和忘记命令如果您已经在扩展另一个类

首选“扩展线程”:

如果必须重写oracle文档页中列出的任何线程方法

通常,您不需要重写线程行为。因此,在大多数情况下,首选实现Runnable。

另一方面,使用高级ExecutorService或ThreadPoolExecutorServiceAPI提供了更多的灵活性和控制。

看看这个SE问题:

ExecutorService vs休闲线程生成器

您应该实现Runnable,但如果您在Java5或更高版本上运行,则不应该使用新线程启动它,而是使用ExecutorService。有关详细信息,请参阅:如何在Java中实现简单线程。

如果你想分开你的关注点,比如,如果你想创建任务,而谁是处理你任务的工作人员对你来说无关紧要,那么使用Runnable并将你的工作定义为任务。就像使用executor服务一样,您以Runnable/callable的形式定义任务,并将它们交给executor,然后由executor负责。

如果您想自己创建一个工作线程并将任务分配给该工作线程,那么您需要对工作线程进行更多的控制,然后使用thread类。

在极少数情况下,您只运行一次,应该因为DRY而扩展Thread。如果多次调用它,则应实现Runnable,因为不应重新启动同一线程。

tl;dr:implements Runnable更好。然而,警告很重要。

一般来说,我建议使用Runnable而不是Thread这样的工具,因为它允许您保持工作与并发选择之间的松散耦合。例如,如果您使用了一个Runnable,并且稍后决定它实际上不需要自己的Thread,那么您可以调用threadA.run()。

注意:在这里,我强烈反对使用原始线程。我更喜欢使用Callables和FutureTasks(来自javadoc:“可取消的异步计算”)。现代并发支持的超时、适当取消和线程池的集成对我来说都比成堆的原始线程有用得多。

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

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

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

因此,如果我们用threadA替换它们的runable,我们会得到以下结果:

new FutureTask<Object>(threadA, null)

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

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

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());