从我在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

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


当前回答

我们可以重新访问我们希望我们的类表现为线程的基本原因吗?根本没有理由,我们只是想执行一个任务,最有可能是在异步模式下,这恰恰意味着任务的执行必须从我们的主线程和主线程分支,如果提前完成,可能会或可能不会等待分支路径(任务)。

如果这就是全部目的,那么我在哪里看到需要专门的线程。这可以通过从系统的线程池中提取一个RAW线程并分配给它我们的任务(可能是我们类的一个实例)来完成,就是这样。

因此,让我们遵循OOP概念,编写一个所需类型的类。做事有很多方法,用正确的方式做事很重要。

我们需要一个任务,所以写一个可以在线程上运行的任务定义。所以使用Runnable。

始终记住,机具专门用于传递行为,扩展用于传递特性/属性。

我们不希望线程的属性,而是希望我们的类作为一个可以运行的任务。

其他回答

随着Java8的发布,现在有了第三个选项。

Runnable是一个函数接口,这意味着可以使用lambda表达式或方法引用创建它的实例。

您的示例可以替换为:

new Thread(() -> { /* Code here */ }).start()

或者如果要使用ExecutorService和方法引用:

executor.execute(runner::run)

这些不仅比示例要短得多,而且还具有其他答案中所述的使用Runnable over 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());

故事的寓意:

仅当您想要覆盖某些行为时才继承。

或者更确切地说,它应该被理解为:

继承更少,接口更多。

我们可以重新访问我们希望我们的类表现为线程的基本原因吗?根本没有理由,我们只是想执行一个任务,最有可能是在异步模式下,这恰恰意味着任务的执行必须从我们的主线程和主线程分支,如果提前完成,可能会或可能不会等待分支路径(任务)。

如果这就是全部目的,那么我在哪里看到需要专门的线程。这可以通过从系统的线程池中提取一个RAW线程并分配给它我们的任务(可能是我们类的一个实例)来完成,就是这样。

因此,让我们遵循OOP概念,编写一个所需类型的类。做事有很多方法,用正确的方式做事很重要。

我们需要一个任务,所以写一个可以在线程上运行的任务定义。所以使用Runnable。

始终记住,机具专门用于传递行为,扩展用于传递特性/属性。

我们不希望线程的属性,而是希望我们的类作为一个可以运行的任务。

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