“实现Runnable”与“扩展线程”在Java

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

实现#0:

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

或者,与扩展#0:

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

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

749445 次浏览

我不是专家,但我可以想到实现Runnable而不是扩展Thread的一个原因:Java只支持单继承,因此您只能扩展一个类。

编辑:这最初也说“实现接口需要更少的资源”,但无论如何都需要创建一个新的Thread实例,所以这是错误的。

是的:实现Runnable是首选的方式,IMO。你并没有真正专门化线程的行为。你只是给它一些东西来运行。这意味着组成哲学“更纯粹”的方式。

实际术语中,这意味着您可以实现Runnable并从另一个类扩展……从Java8开始,您还可以通过lambda表达式实现Runnable

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

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

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

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

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

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

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

所以,如果我们用你的threadA替换他们的runnable,我们会得到以下结果:

new FutureTask<Object>(threadA, null)

另一个允许您更接近Runnables的选项是线程池执行器。您可以使用执行方法传入Runnable以执行“未来某个时候的给定任务”。

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

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

您应该实现Runnable,但如果您运行的是Java5或更高版本,则不应以new Thread开头,而应使用执行人服务。有关详细信息,请参阅:如何在Java中实现简单线程

有一件事我很惊讶还没有提到,那就是实现Runnable使你的类更加灵活。

如果你扩展线程,那么你正在做的操作总是会在一个线程中。然而,如果你实现了Runnable,它就不必了。你可以在线程中运行它,或者将其传递给某种执行器服务,或者只是将其作为单线程应用程序中的任务传递(也许稍后会运行,但在同一线程中)。如果你只是使用Runnable,选择比你将自己绑定到Thread要开放得多。

将Thread类与Runnable实现分离还可以避免线程和run()方法之间潜在的同步问题。单独的Runnable通常会在引用和执行可运行代码的方式上提供更大的灵活性。

故事的寓意:

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

或者更确切地说,应读作:

继承少,接口多。

可运行,因为:

  • 为系统提供了更多的灵活性可运行实现以扩展另一个类
  • 将代码与执行
  • 允许您运行您的可从线程池运行事件线程,或以任何其他方式在《未来》

即使您现在不需要这些,将来也可能需要。由于重写Thread没有任何好处,Runnable是一个更好的解决方案。

我想说还有第三种方法:

public class Something {
public void justAnotherMethod() { ... }
}
new Thread(new Runnable() {public void run() {instanceOfSomething.justAnotherMethod();}}).start();

也许这受到我最近大量使用Javascript和ActionScript 3的影响,但这样你的类就不需要实现像Runnable这样非常模糊的接口。

是的。如果调用ThreadA调用,则不需要调用start方法,而run方法是调用ThreadA类后才调用。但是如果使用ThreadB调用则需要调用run方法的启动线程。如果你有任何帮助,请回复我。

由于上述所有原因,我发现使用Runnable是最有用的,但有时我喜欢扩展Thread,这样我就可以创建自己的线程停止方法并直接在我创建的线程上调用它。

Java不支持多重继承,因此如果您扩展Thread类,则不会扩展其他类。

例如:如果您创建一个小程序,那么它必须扩展Applet类,因此在这里创建线程的唯一方法是通过实现Runnable接口

这里的每个人似乎都认为实现Runnable是要走的路,我并不是真的不同意他们的观点,但在我看来,扩展Thread也是有道理的,事实上你已经在代码中演示了它。

如果你实现了Runnable,那么实现Runnable的类对线程名没有控制权,可以设置线程名的是调用代码,如下所示:

new Thread(myRunnable,"WhateverNameiFeelLike");

但是如果您扩展Thread,那么您可以在类本身中管理它(就像在您的示例中将线程命名为“ThreadB”一样)。在这种情况下,您:

A)可能会给它一个更有用的名称用于调试目的

B)强制将该名称用于该类的所有实例(除非您忽略它是一个线程的事实并将其作为Runnable执行上述操作,但无论如何我们在这里谈论的是约定,因此可以忽略我感觉到的可能性)。

例如,您甚至可以获取其创建的堆栈跟踪并将其用作线程名称。这可能看起来很奇怪,但取决于您的代码结构,它对调试目的非常有用。

这可能看起来像是一件小事,但是你有一个非常复杂的应用程序,有很多线程,突然间事情“停止了”(要么是因为死锁的原因,要么是因为网络协议中的缺陷,这可能不太明显-或者其他无穷无尽的原因),然后从Java获得堆栈转储,所有线程都被称为“Thread-1”,“Thread-2”,“Thread-3”并不总是非常有用(这取决于你的线程是如何结构化的,以及你是否可以通过堆栈跟踪有效地分辨出哪个是哪个-如果你使用多个线程组都运行相同的代码,并不总是可能的)。

话虽如此,您当然也可以通过创建线程类的扩展来以通用的方式执行上述操作,该扩展将其名称设置为其创建调用的堆栈跟踪,然后将其与Runnable实现一起使用,而不是标准的java Thread类(见下文),但除了堆栈跟踪之外,线程名称中可能还有更多上下文特定信息对调试有用(例如,对它可以处理的许多队列或套接字之一的引用,在这种情况下,您可能更喜欢专门针对该情况扩展Thread,以便您可以让编译器强制您(或使用您的库的其他人)传递某些信息(例如队列)/socket问题)在名称中使用)。

下面是一个以调用堆栈跟踪为名称的泛型线程的示例:

public class DebuggableThread extends Thread {private static String getStackTrace(String name) {Throwable t= new Throwable("DebuggableThread-"+name);ByteArrayOutputStream os = new ByteArrayOutputStream();PrintStream ps = new PrintStream(os);t.printStackTrace(ps);return os.toString();}
public DebuggableThread(String name) {super(getStackTrace(name));}
public static void main(String[] args) throws Exception {System.out.println(new Thread());System.out.println(new DebuggableThread("MainTest"));}}

这是比较两个名称的输出示例:

Thread[Thread-1,5,main]Thread[java.lang.Throwable: DebuggableThread-MainTestat DebuggableThread.getStackTrace(DebuggableThread.java:6)at DebuggableThread.<init>(DebuggableThread.java:14)at DebuggableThread.main(DebuggableThread.java:19),5,main]

这可能不是答案,但无论如何;还有一种创建线程的方法:

Thread t = new Thread() {public void run() {// Code here}}

Runnable是一个接口,而Thread是实现了这个接口的类。从设计的角度来看,任务的定义方式和执行方式应该完全分开。前者是Runnalbe实现的责任,后者是Thread类的工作。在大多数情况下,实现Runnable是正确的做法。

那么多好答案,我想在此添加更多。这将有助于理解Extending v/s Implementing Thread
扩展将两个类文件紧密绑定,可能会导致一些非常难以处理的代码。

两种方法都做同样的工作,但有一些差异。
最常见的区别是

  1. 当您扩展Thread类时,之后您就不能扩展您需要的任何其他类。(如您所知,Java不允许继承多个类)。
  2. 当您实现Runnable时,您可以为您的类节省空间,以便将来或现在扩展任何其他类。

然而,实现Runnable和扩展Thread之间的一个重要区别是
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.

下面的例子将帮助你更清楚地理解

//Implement Runnable Interface...class ImplementsRunnable implements Runnable {
private int counter = 0;
public void run() {counter++;System.out.println("ImplementsRunnable : Counter : " + counter);}}
//Extend Thread class...class ExtendsThread extends Thread {
private int counter = 0;
public void run() {counter++;System.out.println("ExtendsThread : Counter : " + counter);}}    
//Use the above classes here in main to understand the differences more clearly...public class ThreadVsRunnable {
public static void main(String args[]) throws Exception {// Multiple threads share the same object.ImplementsRunnable rc = new ImplementsRunnable();Thread t1 = new Thread(rc);t1.start();Thread.sleep(1000); // Waiting for 1 second before starting next threadThread t2 = new Thread(rc);t2.start();Thread.sleep(1000); // Waiting for 1 second before starting next threadThread t3 = new Thread(rc);t3.start();
// Creating new instance for every thread access.ExtendsThread tc1 = new ExtendsThread();tc1.start();Thread.sleep(1000); // Waiting for 1 second before starting next threadExtendsThread tc2 = new ExtendsThread();tc2.start();Thread.sleep(1000); // Waiting for 1 second before starting next threadExtendsThread tc3 = new ExtendsThread();tc3.start();}}

上述程序的输出。

ImplementsRunnable : Counter : 1ImplementsRunnable : Counter : 2ImplementsRunnable : Counter : 3ExtendsThread : Counter : 1ExtendsThread : Counter : 1ExtendsThread : Counter : 1

在Runnable接口方法中,只创建了一个类的实例,并且它已被不同的线程共享。因此,每次线程访问都会增加计数器的值。

然而,线程类方法,你必须为每个线程访问创建单独的实例。因此,为每个类实例分配不同的内存,每个类实例都有单独的计数器,值保持不变,这意味着不会发生增量,因为没有一个对象引用是相同的。

何时使用Runnable?
当你想从线程组访问相同的资源时,使用Runnable接口。避免在这里使用Thread类,因为创建多个对象会消耗更多的内存,从而成为很大的性能开销。

实现Runnable的类不是线程,只是一个类。要使Runnable成为Thread,您需要创建Thread的实例并将自身作为目标传递。

在大多数情况下,如果您仅计划覆盖run()方法而不覆盖其他Thread方法,则应使用Runnable接口。这很重要,因为类不应该被子类化,除非程序员打算修改或增强类的基本行为。

当需要扩展超类时,实现Runnable接口比使用Thread类更合适。因为我们可以在实现Runnable接口制作线程的同时扩展另一个类。

我希望这将有助于!

这就是固体中的S:单一责任。

线程体现了一段代码异步执行运行上下文(如在执行上下文中:堆栈帧、线程ID等)。理想情况下,一段代码应该是相同的实现,无论是同步还是异步

如果将它们捆绑在一个实现中,则为结果对象提供了两个无关更改原因:

  1. 应用程序中的线程处理(即查询和修改执行上下文)
  2. 由一段代码实现的算法(可运行部分)

如果你使用的语言支持部分类或多重继承,那么你可以将每个原因隔离在它自己的超类中,但归结为组成两个对象相同,因为它们的特征集不重叠。这是理论。

在实践中,一般来说,程序不需要携带比必要更多的复杂性。如果您有一个线程在处理特定任务,而不更改该任务,那么将任务分开类可能没有意义,您的代码仍然更简单。

Java的上下文中,由于工具是已经在那里,直接从独立的Runnable类开始,并将它们的实例传递给Thread(或Executor)实例可能更容易。一旦使用进入该模式,使用(甚至阅读)并不比简单的可运行线程案例更难。

如果您想实现或扩展任何其他类,那么Runnable接口是最可取的,否则,如果您不希望任何其他类扩展或实现,那么Thread类是更可取的。

最常见的区别是

输入图片描述

当你extends Thread类之后,你不能扩展任何你需要的其他类(如你所知,Java不允许继承多个类)。

当您implements Runnable时,您可以为您的类节省空间,以便将来或现在扩展任何其他类。

  • Java不支持多重继承,这意味着你只能在Java中扩展一个类,所以一旦你扩展了Thread类,你就失去了机会,不能在Java中扩展或继承另一个类。

  • 在面向对象编程中,扩展一个类通常意味着添加新功能,修改或改进行为。如果我们不对Thread进行任何修改,则使用Runnable接口代替。

  • Runnable接口表示一个可以由纯线程或执行器或任何其他方式执行的任务。因此,将任务逻辑分离为Runnable而不是Thread是一个很好的设计决策。

  • 将任务分离为Runnable意味着我们可以重用任务,并且还可以自由地从不同的方式执行它。因为你不能在线程完成后重新启动它。再次Runnable vs Thread for任务,Runnable是赢家。

  • Java设计者认识到这一点,这就是为什么执行器接受Runnable作为任务,并且他们有工作线程来执行这些任务。

  • 继承所有Thread方法只是为了表示可以使用Runnable轻松完成的任务而额外的开销。

礼貌javarevisited.blogspot.com

这些是Java中Thread和Runnable之间的一些显着差异。如果您知道Thread与Runnable的任何其他差异,请通过评论分享。我个人在此场景中使用Runnable over Thread,并建议根据您的要求使用Runnable或Callable接口。

然而,显著的区别是。

当您extends Thread类时,您的每个线程都会创建一个唯一的对象并与之关联。当您implements Runnable时,它将同一个对象共享给多个线程。

您想实现接口而不是扩展基类的一个原因是您已经扩展了其他一些类。您只能扩展一个类,但您可以实现任意数量的接口。

如果你扩展Thread,你基本上阻止了你的逻辑被“this”以外的任何其他线程执行。如果你只想让一些线程执行你的逻辑,最好只实现Runnable。

如果您使用runnable,您可以节省空间来扩展到任何其他类。

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

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

因此,让我们遵循OOP的概念并编写我们需要的类型的类。有很多方法可以做事情,以正确的方式做很重要。

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

永远记住implements是专门用来传授行为的,extends是用来传授特征/属性的。

我们不需要线程的属性,而是希望我们的类表现为可以运行的任务。

这在Oracle的定义和启动线程教程中进行了讨论:

你应该使用这些习语中的哪一个?第一个习语,它使用了一个Runnable对象,更通用,因为Runnable对象可以子类化Thread以外的类。第二个习惯用法更容易使用在简单的应用程序中,但受限于您的任务类必须是Thread的后代。本课重点介绍第一个方法,它将Runnable任务与Thread对象分开执行任务。这种方法不仅更灵活,而且它适用于涵盖的高级线程管理API以后再说

换句话说,实现Runnable将在你的类扩展了Thread以外的类的情况下工作。Java不支持多重继承。此外,当使用一些高级线程管理API时,扩展Thread是不可能的。扩展Thread更可取的唯一情况是在一个将来不会更新的小应用程序中。实现Runnable几乎总是更好,因为随着项目的发展,它更灵活。设计更改不会产生重大影响,因为你可以在java中实现许多接口,但只扩展一个类。

由于这是一个非常受欢迎的话题,并且好的答案遍布各地并深入处理,我觉得将其他人的好答案汇编成更简洁的形式是合理的,所以新手可以在前面简单地概述:

  1. 你通常会扩展一个类来添加或修改功能。所以,如果你不想覆盖任何线程行为,那么使用Runnable。

  2. 同样的,如果你不需要来继承线程方法,你可以通过使用Runnable来避免开销

  3. 单一继承:如果你扩展Thread,你不能从任何其他类扩展,所以如果这是你需要做的,你必须使用Runnable。

  4. 将域逻辑从技术手段中分离出来是很好的设计,从这个意义上说,最好有一个Runnable任务将你的任务与你的运行器隔离开来。

  5. 你可以多次执行同一个Runnable对象,但是Thread对象只能启动一次。(也许这就是为什么执行器接受Runnable,但不接受线程的原因。)

  6. 如果你将你的任务开发为Runnable,你就有了现在和未来使用它的所有灵活性。你可以通过Exec的同时也可以通过Thread运行它。你仍然可以像任何其他普通类型/对象一样在同一线程内非并发使用/调用它。

  7. 这也使得在你的单元测试中更容易分离任务逻辑和并发方面。

  8. 如果您对这个问题感兴趣,您可能还对Callable和Runnable之间的差异感兴趣

Thread和runnable的区别.如果我们使用Thread类创建Thread,则线程数等于我们创建的对象数。如果我们通过实现可运行接口来创建线程,那么我们可以使用单个对象来创建多线程。

因此,如果我们的数据不是敏感的,这取决于需求。因此,它可以在多个线程之间共享,我们可以使用Runnable接口。

简单来说就是:如果您实现了接口,这意味着您正在实现它的所有方法,如果您扩展了类,您将继承您选择的方法…在这种情况下,有只有一个名为Run()的方法所以最好实现Runnable接口…

随着Java8的发布,现在有了第三种选择。

Runnable功能接口,这意味着它的实例可以使用lambda表达式或方法引用创建。

您的示例可以替换为:

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

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

executor.execute(runner::run)

这些不仅比您的示例短得多,而且还具有使用Runnable而不是Thread的许多优点,例如单一职责和使用组合,因为您没有专门化线程的行为。如果您只需要Runnable,就像您在示例中所做的那样,这种方式还避免创建额外的类。

在这里加上我的两分钱-总是尽可能使用implements Runnable。以下是关于为什么你不应该使用的两个警告extends Thread

  1. 理想情况下,您永远不应该扩展Thread类;Thread类应该设置为final。至少它的方法像thread.getId()。有关扩展Thread的bug,请参阅这个讨论。

  2. 喜欢解谜的人可以看到扩展Thread的另一个副作用。下面的代码当没有人通知他们时,将打印无法访问的代码。

请看http://pastebin.com/BjKNNs2G

public class WaitPuzzle {
public static void main(String[] args) throws InterruptedException {DoNothing doNothing = new DoNothing();new WaitForever(doNothing).start();new WaitForever(doNothing).start();new WaitForever(doNothing).start();Thread.sleep(100);doNothing.start();while(true) {Thread.sleep(10);}}

static class WaitForever extends  Thread {
private DoNothing doNothing;
public WaitForever(DoNothing doNothing) {this.doNothing =  doNothing;}
@Overridepublic void run() {synchronized (doNothing) {try {doNothing.wait(); // will wait forever here as nobody notifies here} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Unreachable Code");}}}
static class DoNothing extends Thread {
@Overridepublic void run() {System.out.println("Do Nothing ");}}}

实现Runnable和扩展Thread之间的一个区别是,通过扩展Thread,每个线程都有一个与之关联的唯一对象,而实现Runnable,许多线程可以共享同一个对象实例。

实现Runnable的类不是线程,只是一个类。要让Thread执行Runnable,您需要创建Thread的实例并将Runnable实例作为目标传入。

在大多数情况下,如果您仅计划覆盖run()方法而不覆盖其他Thread方法,则应使用Runnable接口。这很重要,因为类不应该被子类化,除非程序员打算修改或增强类的基本行为。

当需要扩展超类时,实现Runnable接口比使用Thread类更合适。因为我们可以在实现Runnable接口的同时扩展另一个类来制作线程。但如果我们只是扩展Thread类,我们就不能从任何其他类继承。

实际上,将RunnableThread相互比较是不明智的。

这两者在多线程中具有依赖关系和关系,就像机动车的Wheel and Engine关系一样。

我想说,多线程只有一种方法,有两个步骤。让我说明我的观点。

可运行:
当实现interface Runnable时,它意味着你在不同的线程中创建了run able的东西。现在创建可以在线程中运行的东西(可在线程中运行),并不意味着创建一个线程。
所以类MyRunnable只不过是一个带有void run方法的普通类。它的对象将是一些只有方法run的普通对象,该方法在调用时会正常执行。(除非我们在线程中传递对象)。

主题:
class Thread,我想说一个非常特殊的类,它具有启动新线程的能力,实际上可以通过其start()方法实现多线程。

为什么不明智的比较?
因为我们需要它们来实现多线程。

对于多线程,我们需要两件事:

  • 可以在线程内运行的东西(Runnable)。
  • 可以启动新线程(线程)的东西。

因此,从技术和理论上讲,它们都是启动线程所必需的,一个将运行,一个将让它跑起来(就像机动车的#0)。

这就是为什么你不能用MyRunnable启动一个线程,你需要将它传递给Thread的实例。

但是可以仅使用class Thread创建和运行线程,因为ClassThread实现了Runnable,所以我们都知道Thread内部也是Runnable

最后,ThreadRunnable对于多线程来说是互补的,而不是竞争对手或替代品。

线程保存不打算被访问的行为;

  • 它的同步锁用于连接等。
  • 它有你可以偶然访问的方法。

但是,如果您的子类Thread必须考虑更多的Thread实现。

public class ThreadMain {public int getId() {return 12345678;}
public String getName() {return "Hello World";}
public String getState() {return "testing";}
public void example() {new Thread() {@Overridepublic void run() {System.out.println("id: "+getId()+", name: "+getName()+", state: "+getState());}}.start();}
public static void main(String[] args) {new ThreadMain().example();}}

如果你运行这个,你可能会期望

id: 12345678, name: Hello World, state: testing

但是,您没有调用您认为是的方法,因为您使用的是Thread而不是ThreadMain中的方法,而是您看到的类似

id: 11, name: Thread-0, state: RUNNABLE

最简单的解释是,通过实现Runnable,我们可以将相同的对象分配给多个线程,每个Thread共享相同的对象状态和行为。

例如,假设有两个线程,thread1将一个整数放入数组中,thread2在数组填满时从数组中获取整数。请注意,为了让thread2工作,它需要知道数组的状态,thread1是否填满了它。

实现Runnable使您可以灵活地共享对象,而extends Thread使您可以为每个线程创建新对象,因此由thread1完成的任何更新都会丢失给thread2。

如果我没猜错,它或多或少类似于

接口和抽象类有什么区别?

扩展建立“是一个”关系,接口提供“有一个”能力。

首选项实现Runnable

  1. 如果您不必扩展Thread类并修改Thread API默认实现
  2. 如果你正在执行开火和忘记命令
  3. 如果您已经扩展了另一个类

首选“扩展线程”:

  1. 如果你必须重写这些线程方法中的任何一个,如Oracle留档页所列

一般来说,您不需要覆盖线程行为。所以大多数时候首选实现Runnable

另一方面,使用高级ExecutorServiceThreadPoolExecutorService API提供了更多的灵活性和控制力。

看看这个SE问题:

ExecutorService vs Casual Thread Spawner

对于大多数工作线程来说,最好的方法是将线程完全封装在工作类中,这样就不会有任何外部干扰并导致不需要的和无效的线程/类状态。

我刚刚发布了一个示例,所以我也将与您分享:

/*** This worker can only run once* @author JayC667*/public class ProperThreading {
private final Thread        mThread         = new Thread(() -> runWorkingLoop());   // if you want worker to be able to run multiple times, move initialisation into startThread()private volatile boolean    mThreadStarted  = false;private volatile boolean    mStopRequested  = false;
private final long          mLoopSleepTime;
public ProperThreading(final long pLoopSleepTime /* pass more arguments here, store in members */ ) {mLoopSleepTime = pLoopSleepTime;}
public synchronized void startThread() {if (mThreadStarted) throw new IllegalStateException("Worker Thread may only be started once and is already running!");mThreadStarted = true;mThread.start();}
private void runWorkingLoop() {while (!mStopRequested /* && other checks */ ) {try {// do the magic work hereThread.sleep(mLoopSleepTime);
} catch (final InterruptedException e) {break;} catch (final Exception e) {// do at least some basic handling here, you should NEVER ignore exception unless you know exactly what you're doing, and then it should be commented!}}}
public synchronized void stopThread() {if (!mThreadStarted) throw new IllegalStateException("Worker Thread is not even running yet!");mStopRequested = true;mThread.interrupt();}
}

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

通过扩展线程类,派生类不能扩展任何其他基类,因为java只允许单继承。相反,通过实现可运行接口,类仍然扩展了其他基类。

实现Runnable和扩展Thread之间最显着的区别如下:

通过扩展Thread,派生类本身就是一个线程对象,而实现Runnable接口则将同一个对象共享给多个线程。

扩展线程和实现Runnable的区别是:

在此处输入图片描述

线程类定义了几个扩展类可以overriden的方法。但是要创建线程,我们必须覆盖run()方法。同样也适用于Runnable

但是Runnable是创建线程的首选方法。主要原因是:

  1. 由于Runnable是一个接口,您可以扩展其他类。但是如果您扩展Thread,那么该选项就消失了。

  2. 如果您没有修改或增强大量Thread功能,并且扩展Thread类不是首选方式。

您可以共同使用它们

示例:

public class A implements Runnable{
@Overridepublic void run() {

while(true){System.out.println("Class A is running");try {Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
}


public class Test {
public static void main(String[] args) {
Thread myThread =new Thread(new A());// 1myThread.start();System.out.println(" executed after thread A");//will never be reached
}
}

我想说的是,实际任务是与线程解耦的。在Runnable的情况下,我们可以将任务传递给Thread、Execator框架等,而在扩展Thread的情况下,任务与线程对象本身耦合。扩展Thread的情况下,无法完成任务隔离。就像我们将任务刻录到Thread对象,就像IC芯片一样(更具体地说,不会获得任何任务句柄)。

Thread和Runnable之间的主要区别是:-线程类似:Worker(执行Runnable)-Runnable类似于:作业(由线程执行)

1.扩展线程接口,就像你让你的类只充当一个线程。你的新类将像一个增强的线程。

jshell> public class Test extends Thread{...> public Test(String name){...> super(name);...> }...> public void run(){...> System.out.println(Thread.currentThread().getName());...> }...> }|  created class Test
jshell> Test t1=new Test("MyThread");t1 ==> Thread[MyThread,5,main]

它创建一个线程,而不是Test对象。所以它的行为就像一个线程。您不能在线程之间共享Test类的实例。

2.实现可运行接口。

jshell> public class Test1 implements Runnable{...> public void run(){...> System.out.println(Thread.currentThread().getName());...> }...> public String getName(){...> return "testing";}...> }|  created class Test1
jshell> Test1 t1=new Test1();t1 ==> Test1@396a51ab  --> this creates Test1 object.

这个对象可以跨线程共享,

jshell> Thread t1=new Thread(t1,"Hai");t ==> Thread[Hai,5,main]
jshell> Thread t=new Thread(t1,"Hai");t ==> Thread[Hai,5,main]

我认为已经有很多关于这个话题的讨论,认为这可能对基础知识有帮助。

如果你想分离你的关注点,比如,如果你想创建任务,谁是处理你任务的工作者对你来说并不重要,那么使用Runnable并将你的工作定义为任务。就像你使用执行器服务时一样,你以Runnable/可调用的形式定义任务,并将它们交给执行器服务,然后执行器服务将承担工作者的责任。

如果您想自己创建一个工作线程并为该工作线程分配您的任务,您希望更多地控制工作线程,然后使用Thread类。