当前位置:  开发笔记 > 编程语言 > 正文

Java中的"实现Runnable"与"扩展线程"

如何解决《Java中的"实现Runnable"与"扩展线程"》经验,为你挑选了24个好方法。

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

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



1> Jon Skeet..:

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

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


确切地说,很好.我们试图通过扩展它来在Thread中覆盖什么行为?我认为大多数人都不会试图覆盖任何行为,而是试图使用Thread的行为.
作为旁注,如果你实例化一个Thread并且没有调用它的start()方法,你就会在Java <5中创建一个内存泄漏(Runnables不会发生这种情况):http://stackoverflow.com/questions/107823/为什么 - 是 - 我的Java程序,泄漏内存,当-I-呼叫运行上一个线程对象
用Sierra和Bates的话来说,实现Runnable的一个主要好处就是你在建筑上分离了"跑步者"的"工作".
Runnable的一个小优点是,如果在某些情况下你不关心,或者不想使用线程,并且你只想执行代码,你可以选择简单地调用run().例如(非常手绘)`if(numberCores> 4)myExecutor.excute(myRunnable); 否则myRunnable.run()`
@ user949300你也可以用`extends Thread`做到这一点,如果你不想要线程,你为什么要实现`Runnable` ...
我还想补充一点,现在通过实现Runnable而不是扩展Thread,您可以轻松地使用线程池,计时器,调度程序和执行程序的未来重构.
@ m0skit0:如果你不关心你的代码表达你感兴趣的内容并不重要.我个人非常关心这一点.是的,你*可以*扩展`Thread`只是为了实现`Runnable` ...哎呀,你可以将一个`Thread`实例传递给另一个`Thread`实例的构造函数.这并不意味着这是一个好主意.
@somefolk:嗯,感觉可怕的错误有公共方法开始与`_`,以及具有'开始()`和`_start()`是真的**作为是有所谓的实例方法混淆...`_sleep( )`,当有一个静态的'sleep`方法时.并不是说你的`_sleep`方法实际上*对任何实例都做了*.总的来说,到目前为止,这对我来说是一个坏主意.
"实用术语"位的另一个补充:"Runnable"实际上是对任何代码位的封装.在某些情况下,即使不在新线程的上下文中,运行`Runnable`也许很有用.
@ H2ONaCl不,绝对相反.优先于继承的组合说*反对*扩展任何东西.
@ m0skit0:*什么*不是实现`Runnable`的有效参数?如果要表示"可以运行的东西",则实现`Runnable` - 它不一定必须在新线程中.
请注意,有时您(概念上)*是*创建"跑步者",而不是"工作".例如,如果您正在编写线程池(无论出于何种原因).

2> Bob Cross..:

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

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

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

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

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

Future f = new FutureTask(runnable, null)


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

new FutureTask(threadA, null)


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

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

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


这比接受的答案恕我直言.有一件事:您拥有的代码片段并没有关闭执行程序,我看到数百万个问题导致人们错误,每次他们想要生成任务时都会创建一个新的Executor.`es`会更好地作为静态(或注入)字段,因此它只会被创建一次.
@artbristol,谢谢!我对新的Executor没有异议(我们在代码中做了你的建议).在写原始答案时,我试图编写与原始片段类似的最小代码.我们不得不希望这些答案的许多读者将它们用作跳跃点.我不是要为javadoc写一个替代品.我正在为它有效地编写营销材料:*如果你喜欢这种方法,你应该看到我们提供的所有其他伟大的东西......!*
我知道我对此有点迟了,但直接处理`FutureTask`通常不是你想做的.当你向他们提交`Runnable` /`Callable`时,ExecutorService将为你创建适当的`Future`.同样,对于`ScheduledExecutorService`和`ScheduledFuture`,当你'调度'一个`Runnable` /`Callable`时.
@Powerlord,我的目的是尽可能地制作与OP匹配的代码片段.我同意新的FutureTask不是最佳的,但出于解释的目的这一点很清楚.

3> panzerschrec..:

故事的道德启示:

仅在您要覆盖某些行为时继承.

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

继承少,界面更多.


从Thread继承时,几乎总是想要覆盖`run()`方法的行为.

4> Rupesh Yadav..:

那么多好的答案,我想在此添加更多.这有助于理解Extending v/s Implementing Thread.
Extends非常紧密地绑定两个类文件,并且可能会导致很难处理代码.

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

    当您扩展Thread类时,之后您无法扩展您需要的任何其他类.(如您所知,Java不允许继承多个类).

    实现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接口方法中,只创建了一个类的一个实例,并且它已由不同的线程共享.因此,对于每个线程访问,计数器的值都会递增.

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

什么时候使用Runnable?
如果要从线程组访问相同的资源,请使用Runnable接口.避免在这里使用Thread类,因为多个对象创建会占用更多内存,并且会成为一个很大的性能开销.

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

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

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

我希望这个能帮上忙!


你的代码显然是错误的.我的意思是,它做它做的事情,但不是你打算展示的.
为了澄清:对于可运行的情况,您使用了相同的ImplementsRunnable实例来启动多个线程,而对于Thread情况,您创建了不同的ExtendsThread实例,这显然会导致您显示的行为.main方法的后半部分应该是:`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();`它更清楚吗?
我还不了解你的意图,但我的观点是,如果你创建了多个ExtendsThread实例 - 它们都将返回1(如你所示).你可以通过在那里做同样的事情来获得Runnable的相同结果,即创建ImplementsRunnable的多个实例.
这里发布的代码具有误导性,可能导致头痛.感谢@zEro清理了一下.
@zEro嗨,我来自未来.鉴于您的代码版本也有"Thread"递增,是语句`通过扩展Thread,每个线程都有一个与之关联的唯一对象,而实现Runnable,许多线程可以共享同一个对象实例`然后错误?如果没有,那么证明这一点的案例是什么?
@EvilWashingMachine:长时间不活动......只是看到了这个.我已经将对象的hashcode添加到print语句中...`+"hashcode:"+ this.hashCode()`
@EvilWashingMachine在此处查看结果:http://goo.gl/ub2L8G您将观察到,使用ExtendsThread(ET),我们正在创建不同的线程,作为正在运行的不同对象(**提示**:哈希码),所有计数器的计数器始终初始化为零,然后递增为1。而使用ImplementsRunnable,我们将创建一个ImplementsRunnable实例(即对象; **提示::哈希码),然后使用3个不同的线程对其*运行* 。
@zEro`ExtendsThread tc1 =新的ExtendsThread(); tc1.start(); Thread.sleep(1000); //等待1秒钟,然后再开始下一个线程ExtendsThread tc2 = new ExtendsThread(); tc2.start(); `

5> Herms..:

我还没有提到一件令人惊讶的事情是,实施Runnable使你的课程变得更加灵活.

如果你扩展线程,那么你正在做的动作总是在一个线程中.但是,如果你实现Runnable它不一定是.您可以在一个线程中运行它,或者将它传递给某种执行器服务,或者只是作为单个线程应用程序中的任务传递它(可能在以后运行,但在同一个线程内).如果您使用的Runnable话,选项会比您自己绑定的要多得多Thread.


没错,但是`Thread`增加了许多你不需要的额外东西,并且在许多情况下不需要.你总是更好地实现与你实际做的相匹配的界面.
好吧,你实际上也可以使用`Thread`对象做同样的事情,因为`Thread实现了Runnable` ... ;-)但是用"Runnable"来做这些事情"感觉更好"而不是用`Thread`做它们!

6> Nidhish Kris..:

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

最常见的区别是

在此输入图像描述

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

当您implements Runnable,您可以为您的班级节省空间,以便将来或现在扩展任何其他课程.

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

在面向对象的编程中,扩展类通常意味着添加新功能,修改或改进行为.如果我们没有在Thread上进行任何修改,那么请改用Runnable接口.

Runnable接口表示可以由普通线程或执行器或任何其他方式执行的任务.所以将Task作为Runnable与Thread进行逻辑分离是一个很好的设计决策.

将任务分离为Runnable意味着我们可以重用该任务,并且可以自由地从不同的方式执行它.因为一旦完成,你就无法重启.再次Runnable vs Thread for task,Runnable是胜利者.

Java设计者认识到这一点,这就是Executors接受Runnable作为Task的原因,他们有工作线程来执行这些任务.

继承所有Thread方法只是用于表示可以使用Runnable轻松完成的Task的额外开销.

礼貌来自javarevisited.blogspot.com

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

但是,显着的区别是.

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



7> Saif..:

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

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

我想说,只有一种方法可以通过两个步骤实现多线程.让我说明一下.

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

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

比较为什么不明智?
因为我们需要它们用于多线程.

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

可以在Thread(Runnable)中运行的东西.

可以启动新线程(线程)的东西.

因此从技术上和理论上讲,它们都是启动螺纹所必需的,一个将运行,一个将使其运行(如Wheel and Engine机动车辆).

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

但是有可能只使用class Thread因为类Thread实现而创建和运行一个线程,Runnable所以我们都知道Thread也是一个Runnable内部.

最后Thread,Runnable它们是多线程而不是竞争对手或替代品的补充.


究竟!这应该是公认的答案.顺便说一下,我认为这个问题已被编辑,而且"ThreadA"已经不再有意义了

8> Fabian Steeg..:

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


如果您只想启动单个线程,我不认为ExecutorService会有用.
如果我们知道它将是一个单线程,那么使用任何多线程的重点是什么.所以我们假设我们有多个线程,这个答案很有价值.

9> Powerlord..:

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

编辑:这最初说"实现一个接口需要更少的资源".同样,但你需要创建一个新的Thread实例,所以这是错误的.



10> Bart van Heu..:

我想说还有第三种方式:

public class Something {

    public void justAnotherMethod() { ... }

}

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

也许这会受到我最近大量使用Javascript和Actionscript 3的影响,但这样你的类就不需要实现一个非常模糊的界面了Runnable.


这不是第三种方式.您仍在实施Runnable,只是匿名执行.
@BartvanHeukelom这很方便,但并没有什么不同.您可以使用任何类型的嵌套类来执行此操作,即内部类,本地类和lambda表达式.
@Don Roby:哪个不同.它通常很方便,您可以使用包含类/方法中的字段和最终局部变量.
是的,很方便.

11> Alex..:

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

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

您的示例可以替换为:

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

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

executor.execute(runner::run)

这些不仅比您的示例短得多,而且还具有其他使用Runnable过的答案中所述的许多优点Thread,例如单一责任和使用组合,因为您没有专门化线程的行为.如果您需要的所有内容Runnable与示例中一样,这种方式也可以避免创建额外的类.



12> starblue..:

实例化一个接口可以更清晰地分离代码和线程的实现,所以我更喜欢在这种情况下实现Runnable.



13> AntonyM..:

这里的每个人似乎都认为实现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.(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]


我的观点是"如果你实现Runnable,那么实现Runnable的类无法控制线程名称......"显然是错误的.实现`Runnable`的类确实可以控制线程名称,因为运行代码的线程按照定义是当前线程(并且*通过安全检查*的任何代码都可以控制线程名称).考虑到你把你的一半帖子用于"omg,线程名称怎么样!",这看起来有点大不了.
`Thread.currentThread()的setName( "WhateverNameiFeelLike");`

14> n13..:

Runnable因为:

为Runnable实现提供了更大的灵活性来扩展另一个类

将代码与执行分开

允许您从线程池,事件线程或将来以任何其他方式运行runnable.

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



15> Jörg..:

由于这是一个非常受欢迎的主题,而且好的答案遍布各处并深入处理,我觉得将其他人的好答案汇编成更简洁的形式是合理的,因此新人有一个简单的概述:

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

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

    单继承:如果扩展Thread,则无法从任何其他类扩展,因此如果您需要这样做,则必须使用Runnable.

    将域逻辑与技术手段分开是一种很好的设计,从这个意义上说,让Runnable任务你的任务与你的跑步者隔离开来是更好的选择.

    您可以多次执行相同的Runnable 对象,但是,Thread对象只能启动一次.(也许是原因,为什么Executors接受Runnables,但不接受Threads.)

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

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

    如果您对此问题感兴趣,您可能也对Callable和Runnable之间的区别感兴趣.



16> Raman Gupta..:

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

在此输入图像描述



17> Sionnach733..:

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

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

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



18> Ravindra bab..:

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

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

extends建立" Is A "关系和接口提供" Has a "能力.

首选实现Runnable:

    如果您不必扩展Thread类并修改Thread API的默认实现

    如果您正在执行fire and forget命令

    如果您已经在扩展另一个班级

首选" 扩展线程 ":

    如果必须覆盖oracle文档页面中列出的任何这些Thread方法

通常,您不需要重写Thread行为.因此,大多数情况下,实现Runnable是首选.

另外,使用高级ExecutorServiceThreadPoolExecutorServiceAPI可提供更多灵活性和控制.

看看这个SE问题:

ExecutorService与Casual Thread Spawner



19> Shababb Kari..:

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

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

实现Runnable允许您具有共享对象的这种灵活性,extends Thread而使您为每个线程创建新对象,因此thread1完成的任何更新都会丢失给thread2.



20> Govula Srini..:

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



21> didierc..:

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

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

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

    应用程序中的线程处理(即查询和修改执行上下文)

    由一段代码(可运行部分)实现的算法

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

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

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



22> 小智..:

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

如果你扩展Thread,你基本上阻止你的逻辑被"this"之外的任何其他线程执行.如果您只想要一些线程来执行您的逻辑,那么最好只实现Runnable.



23> user2771655..:

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



24> dharam..:

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

如果这是整个目的,那么我在哪里可以看到需要专门的线程.这可以通过从系统的线程池中获取RAW线程并为其分配我们的任务(可能是我们类的一个实例)来实现,就是这样.

因此,让我们遵守OOP概念并编写一个我们需要的类.有很多方法可以做到,以正确的方式做事很重要.

我们需要一个任务,所以编写一个可以在Thread上运行的任务定义.所以使用Runnable.

永远记住implements是专门用于传授行为并extends用于传授特征/属性.

我们不想要线程的属性,而是希望我们的类可以作为可以运行的任务来运行.

推荐阅读
臭小子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有