Java中的“可运行的实现”与“扩展线程”


2118

从什么时候开始在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

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


57
感谢这个问题,答案消除了我的很多误解。在SO出现之前,我研究了做Java线程的正确方法,并且那里存在许多错误信息/过时的信息。
詹姆斯·麦克马洪

5
您可能想扩展Thread是有一个原因(但我不建议您扩展),您可以抢先处理interrupt()。再说一次,这是一个主意,在正确的情况下可能有用,但是我不建议这样做。
bestsss 2011年

请也很好地解释答案:stackoverflow.com/q/5562720/285594

@bestsss,我想弄清楚您对处理interrupt()可能意味着什么。您是否要覆盖该方法?
鲍勃·克罗斯

8
是的。根据代码,线程A类可以扩展任何类,而线程B类则不能扩展任何其他类
mani deepak 2014年

Answers:


1672

是的:RunnableIMO是首选的工具。您并不是真的专门研究线程的行为。您只是在给它一些东西来运行。这意味着合成是从哲学上讲“更纯净”的方式。

实践方面,它意味着你可以实现Runnable从另一个类扩展为好。


151
没错,很好。我们试图通过扩展覆盖Thread的行为是什么?我认为大多数人不是在试图覆盖任何行为,而是在尝试使用线程的行为。
hooknc 2009年

87
作为边评论,如果你实例化一个线程,不调用它的start()方法,您要创建在Java中<5内存泄漏(这不符合的Runnable发生):stackoverflow.com/questions/107823/...
纳乔·科洛马

25
Runnable的一个小优点是,如果在某些情况下您不关心或不想使用线程,而只想执行代码,则可以选择简单地调用run()。例如(非常手工) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300 2013年

10
@ user949300,您也可以这样做,extends Thread如果您不想使用线程,为什么还要实现Runnable...
m0skit0 2013年

30
用Sierra和Bates来解释,实现Runnable的主要好处是您在架构上将“作业”与“运行器”分开。
8bitjunkie 2014年

569

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

通常,我建议您使用Runnable而不是,Thread因为它可以使您的工作与并发选择保持松散的结合。例如,如果使用a Runnable并在以后决定实际上并不需要它Thread,则可以只调用threadA.run()。

警告:在这里,我强烈建议不要使用原始线程。我更喜欢使用CallablesFutureTasks(来自javadoc:“可取消的异步计算”)。与大量原始线程相比,超时的集成,适当的取消以及现代并发支持的线程池对我来说都更加有用。

后续:有一个FutureTask构造函数,可让您使用Runnables(如果您对此最满意),并且仍可从现代并发工具中受益。 引用javadoc

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

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

因此,如果我们将它们替换runnable为your ,则会threadA得到以下信息:

new FutureTask<Object>(threadA, null)

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

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

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

40
这比接受的答案恕我直言更好。一件事:您拥有的代码片段并未关闭执行程序,我看到数百万个问题使人们误解了该问题,每次他们想要生成任务时都会创建一个新的执行程序。es最好将其作为静态(或注入)字段,以便仅创建一次。
artbristol

7
@artbristol,谢谢!我对新的执行程序没有异议(我们按照您在代码中的建议进行操作)。在编写原始答案时,我试图编写与原始片段类似的最少代码。我们必须希望这些答案的许多读者将它们当作起点。我不是想写一个javadoc的替代品。我正在为此有效地编写市场营销材料:如果您喜欢这种方法,您应该会看到我们必须提供的所有其他出色功能……!
鲍勃·克罗斯

3
我知道我对此事发表评论晚了一点,但是FutureTask直接处理通常不是您想要做的。 ExecutorService旨意建立适当的Future你,当你submit一个Runnable/ Callable他们。对于ScheduledExecutorServices和ScheduledFuture当您schedule使用Runnable/时也是如此Callable
Powerlord

3
@Powerlord,我的意图是使代码片段尽可能地与OP匹配。我同意新的FutureTask不是最佳选择,但出于解释目的很明显。
鲍勃·克罗斯

257

故事的道德启示:

仅在要覆盖某些行为时才继承。

更确切地说,应将其理解为:

继承少,接口多。


1
如果您开始制作并发运行的对象,这应该一直是一个问题!您甚至需要线程对象功能吗?
Liebertee

4
从Thread继承时,几乎总是希望重写该run()方法的行为。
沃伦·露

1
您不能通过覆盖方法java.lang.Thread来覆盖a的行为run()。在那种情况下,您需要重写start()我猜的方法。通常,您只需java.lang.Thread将执行块插入run()方法即可重用的行为。
sura2k

继承不仅用于覆盖某些行为,还用于使用常见行为。相反,越多的覆盖,等级越差。
peja

232

这么多好答案,我想补充一点。这将有助于理解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 thread
    Thread t2 = new Thread(rc);
    t2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread 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 thread
    ExtendsThread tc2 = new ExtendsThread();
    tc2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc3 = new ExtendsThread();
    tc3.start();
 }
}

上面程序的输出。

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

在Runnable接口方法中,仅创建一个类的实例,并且该实例已由不同线程共享。因此,每次线程访问时,counter的值都会增加。

鉴于使用Thread类的方法,您必须为每个线程访问创建单独的实例。因此,为每个类实例分配不同的内存,并且每个实例都有单独的计数器,其值保持不变,这意味着不会增加,因为没有一个对象引用是相同的。

什么时候使用Runnable?
要从线程组访问相同的资源时,请使用Runnable接口。避免在这里使用Thread类,因为创建多个对象会消耗更多的内存,并且会增加性能开销。

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

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

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

我希望这个能帮上忙!


40
您的代码显然是错误的。我的意思是,它完成了它的工作,但没有达到您的预期。
zEro

38
需要说明的是:对于可运行情况,您使用了相同的ImplementsRunnable实例来启动多个线程,而对于线程情况,您正在创建不同的ExtendsThread实例,这显然导致了所显示的行为。您主要方法的第二部分应该是: ExtendsThread et = new ExtendsThread(); Thread tc1 = new Thread(et); tc1.start(); Thread.sleep(1000); Thread tc2 = new Thread(et); tc2.start(); Thread.sleep(1000); Thread tc3 = new Thread(et); tc3.start(); 它更清晰吗?
zEro

19
我还不了解您的意图,但我的意思是,如果您创建ExtendsThread的多个实例-它们都将返回1(如您所示)。通过在此处执行相同的操作,即创建多个ImplementsRunnable实例,可以为Runnable获得相同的结果。
zEro

3
@zEro嗨,我来自未来。鉴于您的代码版本也已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错误?如果没有,那么什么情况证明了这一点?
邪恶的洗衣机

4
此处发布的代码具有误导性,并可能导致头痛。感谢@zEro对其进行了一些整理。
Nikos's

78

尚未引起我惊讶的一件事是实现Runnable使您的类更加灵活。

如果扩展线程,那么您正在执行的操作将始终处于线程中。但是,如果您实施Runnable,则不必如此。您可以在线程中运行它,或将其传递给某种执行服务,或者仅作为任务在单个线程应用程序中传递(可能在以后的时间,但在同一线程中运行)。如果只使用这些选项,则Runnable比将自己绑定到更加开放Thread


6
好吧,实际上您也可以对一个Thread对象执行相同的操作,因为Thread implements Runnable…;-)但是,用a进行此操作Runnable比用a 进行处理“感觉更好” Thread
siegi 2012年

7
是的,但是Thread添加了很多不必要的东西,在很多情况下是不需要的。始终最好实现与实际操作相匹配的接口。
爱马仕(Herms)2012年

74

如果要实现或扩展任何其他类,则Runnable首选接口,否则,如果不希望扩展或实现任何其他类,Thread则最好选择类。

最常见的区别是

在此处输入图片说明

当你 extends Thread上课时,之后您将无法扩展您需要的任何其他课程。(您知道,Java不允许继承多个类)。

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

  • Java不支持多重继承,这意味着您只能在Java中扩展一个类,因此一旦扩展Thread类,您将失去机会,无法扩展或继承Java中的另一个类。

  • 在面向对象的编程中,扩展类通常意味着添加新功能以及修改或改善行为。如果我们不对Thread进行任何修改,请改用Runnable接口。

  • 可运行接口表示可以通过普通线程或执行程序或任何其他方式执行的任务。因此,将Task与Runnable进行逻辑上的分离比选择Thread是一个好的设计决策。

  • 将任务分隔为可运行意味着我们可以重用任务,也可以通过不同的方式来执行它。因为您无法在线程完成后重新启动它。再一次,Runnable vs Thread完成任务,Runnable获胜。

  • Java设计人员意识到了这一点,这就是执行器接受Runnable作为Task的原因,并且他们拥有执行这些任务的工作线程。

  • 继承所有Thread方法是额外的开销,仅用于表示可以通过Runnable轻松完成的Task。

javarevisited.blogspot.com提供

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

但是,明显的区别是。

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


73

其实,这不是明智的比较RunnableThread互相。

这两个在多线程中具有依赖关系 Wheel and Engine机动车辆的关系,。

我要说,只有两个步骤的多线程方法。让我说清楚。

可运行:
在实现interface Runnable时意味着您正在创建run able位于其他线程中的内容。现在创建可以在线程内运行(可在线程内运行)的东西,并不意味着要创建线程。
因此,该类MyRunnable不过是带有void run方法的普通类。它的对象将是一些普通对象,仅具有run在调用时将正常执行的方法。(除非我们在线程中传递对象)。

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

为什么不明智地进行比较?
因为我们都需要它们都具有多线程功能。

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

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

因此,从技术上和理论上来说,它们都是启动线程所必需的,一个线程将运行,一个线程将使其运行(就像Wheel and Engine汽车一样)。

这就是为什么无法启动线程而MyRunnable需要将其传递到的实例的原因Thread

但是可以使用class ThreadClass 创建和运行线程,因为Class Thread实现了,Runnable所以我们都知道它Thread也是一个Runnable内部。

最后ThreadRunnable对于多线程而不是竞争对手或替代产品,彼此互补。


3
究竟!这应该是公认的答案。顺便说一句,我认为这个问题已经过编辑,ThreadA不再有意义
idelvall

接受的答案是更多代表,谢谢您的回复@idelvall
赛义夫,

最好的答案!谢谢!
MichaelYe

44

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


5
如果您只想启动一个线程,我认为ExecutorService不会那么有用。
Powerlord

1
据我了解,一般来说,不再应该自己启动线程,因为将其留给执行程序服务可以使一切变得更加可控(例如,等待线程挂起)。另外,我在问题中没有看到任何暗示它与单个线程有关的信息。
Fabian Steeg,

4
如果我们事先知道它将是一个单线程,那么使用任何多线程有什么意义呢?因此,假设我们有多个线程,并且这个答案很有价值。
zEro

1
@zEro我敢肯定,只有一个事件调度线程是有原因的。我怀疑这是唯一的情况,最好是有一个单独的线程,但可能不是最好有多个线程。
porcoesphino 2013年

33

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

编辑:最初说“实现接口需要更少的资源”。同样,但是您需要以任何一种方式创建一个新的Thread实例,所以这是错误的。


在可运行状态下,我们无法进行网络呼叫,是吗?由于我有android.os.NetworkOnMainThreadException。但是通过使用线程,我可以进行网络调用。如果我错了,请纠正我。
Nabeel Thobani 2014年

@NabeelThobani普通Java不在乎,但听起来像Android。不过,我对Android还不够熟悉。
Powerlord 2014年

1
@NabeelThobani当然可以。可能您不是在使用Runnable创建线程。
m0skit0 2015年

20

我会说还有第三种方式:

public class Something {

    public void justAnotherMethod() { ... }

}

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

也许这受我最近大量使用Javascript和Actionscript 3的影响,但是通过这种方式,您的类无需实现类似的模糊接口Runnable


38
这实际上不是第三种方式。您仍在实现Runnable,只是匿名进行。
唐·罗比

2
@唐·罗比:哪有不同。这通常很方便,您可以使用包含的类/方法中的字段和最终局部变量。
Bart van Heukelom

6
@BartvanHeukelom很方便,但没有什么不同。您可以使用任何类型的嵌套类(即内部类,局部类和lambda表达式)来执行此操作。
xehpuk

18

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

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

您的示例可以替换为:

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

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

executor.execute(runner::run)

这些不仅比您的示例短很多,而且还具有使用Runnableover的其他答案中所述的许多优点Thread,例如单一职责和使用组合,因为您没有专门研究线程的行为。如果您只需要一个类,这种方法还可以避免创建额外的类Runnable像在示例中那样进行操作,。


这个答案需要解释。经过一番困惑后,我得出结论认为这() -> {}应该代表某人需要的自定义逻辑?因此,最好这样说() -> { /* Code here */ }
制造商

17

实例化一个接口可以使您的代码与线程实现之间的分隔更加清晰,因此在这种情况下,我更喜欢实现Runnable。


12

这里的每个人似乎都认为实现Runnable是必经之路,我并不完全不同意它们,但是在我看来,还有一种扩展Thread的情况,实际上您已经在代码中进行了演示。

如果实现Runnable,则实现Runnable的类无法控制线程名称,它是可以设置线程名称的调用代码,如下所示:

new Thread(myRunnable,"WhateverNameiFeelLike");

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

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

B)强制将该名称用于该类的所有实例(除非您忽略它是线程的事实并对其进行处理,就好像它是Runnable一样,但是无论如何我们都在这里讨论约定,可以忽略我的那种可能性)。

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

这看起来似乎是一件小事,但是如果您有一个非常复杂的应用程序,其中包含许多线程,并且突然之间所有操作都已“停止”(由于死锁的原因或可能是由于网络协议中的缺陷所致,这种缺陷会更少很明显-或其他无尽的原因),然后从Java中获取所有线程都称为'Thread-1','Thread-2','Thread-3'的堆栈转储并不总是很有用(这取决于线程的使用方式)结构化的信息,以及是否可以仅通过堆栈跟踪有效地分辨出哪个-如果您使用的是多个都运行相同代码的线程组,则并非总是可能的。

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

这是一个以调用堆栈跟踪作为名称的通用线程的示例:

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-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]

H您的意思是什么?您无法在线程执行期间使用上面的代码来获取线程创建的堆栈跟踪(相反,您会得到一个简单的名称,或充其量只是线程启动的堆栈跟踪),但是通过子类化线程,您可以做到这一点并强制执行它甚至需要更多特定于上下文的信息,从而使您对哪个线程可能有问题的具体了解更加具体。
AntonyM 2012年

8
我的观点是:“如果实现Runnable,则实现Runnable的类将无法控制线程名称...”显然是错误的。实现的类Runnable确实可以控制线程名称,因为运行代码的线程根据定义是当前线程(并且任何通过安全检查的代码都可以控制线程名称)。考虑到您将一半的帖子投入到“ omg,线程名称呢!”,这似乎是一件大事。
cHao 2012年

线程名称?没有什么阻止您扩展线程类的。
RichieHH 2014年

11

可运行,因为:

  • 为Runnable实现留出更多灵活性来扩展另一个类
  • 将代码与执行分开
  • 允许您从线程池,事件线程或将来以任何其他方式运行可运行对象。

即使您现在不需要这些,将来也可能。由于覆盖线程没有任何好处,因此Runnable是更好的解决方案。


11

由于这是一个非常受欢迎的话题,并且好的答案被广泛地传播和深入地处理,所以我觉得将其他人的好的答案整理成更简洁的形式是合理的,因此新手可以从头开始轻松地进行概述:

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

  2. 在相同的光,如果你不需要继承 Thread方法,你可以做而没有开销使用Runnable接口。

  3. 单一继承:如果扩展Thread,则不能从任何其他类扩展,因此如果需要这样做,则必须使用Runnable。

  4. 这是很好的设计,从技术手段单独的域的逻辑,在这个意义上,最好是有一个Runnable任务隔离从任务亚军

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

  6. 如果您将任务开发为可运行的,那么现在和将来都有使用它的灵活性。您可以通过Executors和Thread同时运行它。而且,您仍然可以像在任何其他普通类型/对象中一样在同一线程中非并行使用/调用它。

  7. 这使得它也更容易单独任务的逻辑和并发性的方面你的单元测试

  8. 如果您对此问题感兴趣,则可能还对Callable和Runnable之间区别感兴趣。


@Pino是的,线程本身也是一个Runnable。但是,如果将其扩展为仅将其用作可运行对象,那有什么意义呢?为什么不使用没有所有行李的普通Runnable。因此,我认为,如果您扩展Thread,您还可以通过使用它的start方法来执行它,该方法只能使用一次。Nidhish-Krishnan就是要在回答中指出这一点。请注意,我的只是这里的其他答案的汇总或简短摘要。
约尔格


8

Oracle的“ 定义和启动线程”教程中对此进行了讨论:

您应该使用以下哪些习语?第一个使用Runnable对象的习惯用法更为通用,因为Runnable对象可以继承Thread以外的其他类。第二种习惯用法在简单的应用程序中更易于使用,但由于您的任务类必须是Thread的后代而受到限制。本课重点讨论第一种方法,该方法将Runnable任务与执行该任务的Thread对象分开。这种方法不仅更灵活,而且适用于稍后介绍的高级线程管理API。

换句话说,Runnable在您的课程扩展了以外的其他课程的情况下,实现将起作用Thread。Java不支持多重继承。另外,Thread使用某些高级线程管理API时将无法扩展。Thread最好采用扩展的唯一方案是在小型应用程序中,该应用程序将来将不会进行更新。Runnable随着项目的发展,它的灵活性通常会更好。设计更改不会产生重大影响,因为您可以在Java中实现许多接口,而仅扩展一个类。


8

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

例如,假设有两个线程,当数组被填满时,thread1在数组中放置一个整数,thread2从数组中获取整数。请注意,为了使线程2工作,它需要知道数组的状态,无论线程1是否已将其填满。

通过实施Runnable,您可以灵活地共享对象,而extends Thread可以为每个线程创建新对象,因此,线程1所做的任何更新都将丢失到线程2中。


7

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

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

扩展建立“ ”关系并提供“ 具有 ”功能。

首选实现Runnable

  1. 如果您不必扩展Thread类并修改Thread API的默认实现
  2. 如果您正在执行即发即弃命令
  3. 如果您已经在扩展另一堂课

首选“ 扩展线程 ”:

  1. 如果您必须覆盖oracle文档页面中列出的所有这些Thread方法

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

另一方面,使用高级ExecutorServiceThreadPoolExecutorServiceAPI可提供更大的灵活性和控制力。

看看这个SE问题:

ExecutorService与休闲线程Spanner



5

这就是小号SOLID:单一职责。

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

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

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

如果您使用的语言支持部分类或多重继承,则可以将每个原因归为它自己的超类,但是归结为与组成两个对象相同,因为它们的功能集不重叠。这是理论上的。

实际上,一般而言,程序不需要携带不必要的复杂性。如果您有一个线程在执行一项特定任务,而又从未更改过该任务,那么将这些任务划分为单独的类可能没有任何意义,并且您的代码将更加简单。

Java的上下文中,由于该功能已经存在,因此直接从独立的Runnable类开始并将它们的实例传递给Thread(或Executor)实例可能更容易。一旦习惯了这种模式,使用(甚至读取)就不会比简单的可运行线程的情况更难。


5

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

如果扩展Thread,则基本上是在阻止逻辑由“ this”以外的任何其他线程执行。如果只希望某个线程执行您的逻辑,则最好只实现Runnable。


是的,通过实现Runnable接口可以自由地通过扩展任何类来实现自己的逻辑,这就是为什么Runnable相对于Thread类更受青睐。
Akash5288

5

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


5

我们是否可以重新考虑我们希望我们的班级表现得像个基本理由Thread?根本没有理由,我们只是想执行一个任务,很可能是在异步模式下执行,这恰好意味着该任务的执行必须从我们的主线程中分支出来,并且如果提早完成,主线程可能会或可能不会等待用于分支路径(任务)。

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

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

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

始终牢记implements是专门用于赋予行为,extends并用于赋予特征/特性。

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


4

是的,如果您调用ThreadA call,则无需调用start方法,而仅在调用ThreadA类之后才调用run方法。但是如果使用ThreadB调用,则需要为调用run方法启动线程。如果您还有其他帮助,请回复我。



4

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

例如:如果创建一个applet,则它必须扩展Applet类,因此这里创建线程的唯一方法是实现Runnable接口


4

Runnable是一个接口,Thread而是实现此接口的类。从设计的角度来看,应该在如何定义任务和如何执行任务之间有明确的区分。前者是实现的责任,Runnalbe后者是Thread该类的职责。在大多数情况下,实施Runnable是遵循的正确方法。


4

Thread和runnable之间的区别。如果我们使用Thread类创建Thread,则线程数等于我们创建的对象数。如果我们通过实现runnable接口来创建线程,则可以使用单个对象创建多个线程,因此单个对象被多个Thread共享,因此它将占用更少的内存

因此,根据要求,如果我们的数据不敏感。因此它可以在多个Thread之间共享,我们可以使用Runnable接口。


4

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

  1. 理想情况下,您永远不应扩展Thread类。在Thread类应该进行final。至少它的方法像thread.getId()。有关扩展s 的错误,请参见此讨论Thread

  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;
        }

        @Override
        public 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 {

        @Override
        public void run() {
            System.out.println("Do Nothing ");
        }
    } 
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.