"实现可运行的"vs"延伸线"在Java
-
23-08-2019 - |
题
从什么时候我已经花了有线在爪哇,我发现这两种方式编写线程:
与 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
是否有任何显着差别,在这两个区块的代码?
解决方案
是:实现Runnable
是做到这一点,IMO的首选方式。你不是真的专业线程的行为。你只是给它的东西跑。这意味着组合物是在哲学 “更纯” 的路要走。
在实际术语,这意味着可以实现Runnable
和从另一个类延伸为好。
其他提示
tl博士:实现可运行的更好。然而,需要说明的是重要的
在一般情况下,我建议使用一些东西喜欢 Runnable
而不是 Thread
因为它可以让你保持你的工作只有松散耦合你的选择并发。例如,如果您使用 Runnable
并决定以后,这并不事实上需要它自己的 Thread
, 你可以叫threadA.运行().
警告: 在这里,我强烈劝阻使用原始的线。我更喜欢使用 可调用类型 和 FutureTasks (从如果是的话,为什么不试:"一个撤销的异步计算").集成的超时、正确的取消和在线汇集的现代化并发支持都是有用得多我比一堆堆的原螺纹。
后续行动: 还有一个 FutureTask
构造 这可以让你用Runnable(如果这是你最舒服),仍然可以得到的利益的现代化并发的工具。 引用如果是的话,为什么不试:
如果你不需要一个特定的结果,考虑使用建筑的形式:
Future<?> f = new FutureTask<Object>(runnable, null)
所以,如果我们将自己 runnable
与你的 threadA
, 我们得到如下:
new FutureTask<Object>(threadA, null)
另一种选择,可以让你更接近Runnable是 ThreadPoolExecutor.你可以使用 执行 方法通过在一个可运行的执行"的定的任务在将来某个时候。"
如果你想尝试使用一线的游泳池,代码碎片上文将成为如下(使用 执行者。newCachedThreadPool() 工厂的方法):
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
故事的道德:
如果你想重写某些行为的只继承。的
或者而是应当被理解为:
继承以下,界面更加。
好了那么多好的答案,我希望增加更多的这一点。这将有助于理解 Extending v/s Implementing Thread
.
延伸结合两类文件非常密切的,并可能导致一些非常难以处理的代码。
这两种方法做同样的工作,但已有一些差异。
最常见的差别是
- 当你伸线类,在那之后你不能延长的任何其他类,你必需的。(如你所知,Java不允许继承一个多类)。
- 当你实施可运行的,你可以保存空间为您类延长的任何其他类在未来,还是现在。
然而,一个 显着差异 之间实施可运行的,并延伸线是
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
在可运行接口的办法,只有一个类的实例是正在创建的,它已通过不同的线。所以价值计数增加为每一个线程访问。
鉴线类做法,必须创建独立的实例的每一个线程访问。因此,不同的记忆被分配用于每一类实例中,各有单独的反,该数值仍然相同,这意味着没有增加,将会发生,因为没有任何的对象的基准是一样的。
当使用可运行的?
使用可运行的接口,当你想要访问的同样的资源,从该集团的螺纹。避免使用螺纹类在这里,由于多个目的建立消耗更多的存储和它成为一个很大的性开销。
一个类实现可运行的是不是一个线程和只一类。对于一个可运行,成为一个线程,需要创建一个实例线和通过本身作为目标。
在大多数情况下,可运行的界面应当使用如果你是只有计划复盖的 run()
方法并没有其他线的方法。这是重要的,因为类不应该子类,除非程序员打算修改或加强的基本行为类。
当有必要延长超类,实施可运行的接口更适合于使用的线类。因为我们可以扩展的另一个类,同时实施可运行的接口,使一线。
我希望这将帮助!
有一件事情,我很惊讶,尚未提及的是,实施Runnable
让你的类更加灵活。
如果您延长线,那么你正在做的动作总是要在一个线程。但是,如果实现Runnable
它并不一定如此。你可以在一个线程中运行它,或者把它传递给某种执行人的服务,或只是通过它周围的单线程应用程序中的任务(可能在稍后的时间运行,但在同一个线程内)。选项有很多更开放,如果你只是用Runnable
比,如果你绑定自己Thread
。
如果你想要实现或扩展任何其他类然后 Runnable
接口是最可取的,否则,如果你不想任何其他类来扩展或实现后 Thread
类是可取的。
最常见的差别是
当你 extends Thread
类,在那之后你不能延长的任何其他类,你必需的。(如你所知,Java不允许继承一个多类)。
当你 implements Runnable
, 你可以保存空间为您类延长的任何其他类在未来,还是现在。
Java不支持多的遗产,这意味着你只能延长一级的在Java所以一旦你长线类你失去了你的机会和无法扩大或继承另一类。
在面向对象的节目,延伸的一类一般意味着,增加新的功能,并修改或改进行为。如果我们不作出任何修改在线然后使用可运行的接口。
可运行的接口,代表一个任务,它可以执行通过平线或遗嘱执行人或任何其他手段。因此逻辑上分离的任务,因为可运行的比线是一个好的设计的决定。
分离的任务,因为可运行的,这意味着我们可以重复使用的任务,也有的自由来执行它从不同的手段。因为你不能重新启动一个线程一旦完成。再次运行vs线的任务,可运行的是赢家。
Java设计师确认这一点,这就是为什么遗嘱执行人接受运行为的任务,他们有线工作人员执行这些任务。
继承了所有线的方法是额外的开销,只是为了代表的任务,它可能很容易做到与可运行.
从礼貌 javarevisited.blogspot.com
这些都是一些显着的差异之间的线和可运行。如果你知道任何其他差异在线vs可以运行,请它分享通过的意见。我个人使用可运行在线对于这种情况,并建议使用可运行的或可调用的接口,根据您的要求。
然而,显着的差异。
当你 extends Thread
班,你的每一个线程创造了一个独特的对象和与相关联。当你 implements Runnable
, 它的股份相同的对象多线程。
实际上,它是不明智的比较 Runnable
和 Thread
彼此。
这两个具有相关性和关系在多线程只是喜欢 Wheel and Engine
关系的机动车辆。
我会说,只有一个办法对于多线程的两个步骤。让我提出我的观点。
可运行的:
在实现 interface Runnable
这意味着你是创造一些东西, run able
在一个不同的线。现在创造的东西,它可运行的内部螺纹(可运行的内部线),并不意味着创造一个线程。
如此类 MyRunnable
不过是一个普通类 void run
法。它的目的将一些普通的物体与仅仅一个方法 run
这将执行正常时调用。(除非我们通过的对象螺纹)。
线:
class Thread
, 我会说一个非常特殊类有能力开始一个新的线这实际上使多线程的通过它的 start()
法。
为什么不明智的比较?
因为我们需要他们两个多线程。
对于多线程的我们需要两样东西:
- 一些可以运行的内部螺纹(可运行的).
- 东西可以开始一个新的螺纹(螺纹)。
所以在技术上和理论上他们都是有必要启动一个线程,一会 运行 和一个会 让它运行 (喜欢 Wheel and Engine
机动车辆)。
这就是为什么你不能开始一个线程 MyRunnable
你需要通过它的一个实例 Thread
.
但 它能够创建和运行一个线程只使用 class Thread
因为类 Thread
实现了 Runnable
所以我们都知道 Thread
也是 Runnable
内。
最后 Thread
和 Runnable
是的补充彼此的多线程不是竞争对手或更换。
您应该实现Runnable,但如果你是在Java 5或更高的运行,你不应该new Thread
启动,但使用的 ExecutorService的代替。有关详情请参阅:如何在Java中实现简单线程
我不是专家,但我能想到的一个理由来实现Runnable接口,而不是扩展Thread:用Java只支持单继承,所以你只能继承一个类
编辑:这原本所述“实现接口需要较少的资源”。为好,但你需要创建一个新的Thread实例无论哪种方式,所以这是错误的。
我会说还有第三种方法:
public class Something {
public void justAnotherMethod() { ... }
}
new Thread(new Runnable() {
public void run() {
instanceOfSomething.justAnotherMethod();
}
}).start();
也许这是影响有点被我最近的Javascript和ActionScript 3的大量使用,但这样一来你的类并不需要实现像Runnable
一个很模糊的界面。
使用的Java 8的释放,现在有第三个选项。
Runnable
是功能接口,该意味着它的实例可以与lambda表达式或方法的引用来创建。
您例如可以替换为:
new Thread(() -> { /* Code here */ }).start()
,或者如果你想使用ExecutorService
和方法参考:
executor.execute(runner::run)
这不仅比你的例子要短得多,而且还配备了许多在使用Runnable
超过Thread
,如单一职责和使用成分,因为你不专业线程的行为的其他的答案所述的优点。这样也避免了创建一个额外的类,如果你需要的是一个Runnable
正如你在你的例子做的。
实例化界面给您的代码和线程的执行之间的清洁器的分离,所以我更愿意实现在这种情况下可运行。
每个人都在这里似乎认为实现Runnable是要走的路,我真的不跟他们不同意,但也有在我看来扩展Thread,其实你已经有点证明它在你的代码的情况下。
如果您实现Runnable那么实现Runnable拥有的线程名无法控制的类,它是调用代码,可以设置线程的名称,例如:
new Thread(myRunnable,"WhateverNameiFeelLike");
但如果你扩展线程,那么你得到的类本身(就像在你的榜样,你的名字线程“ThreadB”)内管理这个。在这种情况下,你:
A)可能给它一个更有用的名称用于调试目的
B),迫使是该名称中使用该类的所有实例(除非你忽略了一个事实,即它是一个线程,并执行上面就好像它是一个Runnable,但我们在任何这里所说的惯例情况下,从而可以忽略这种可能性我感觉)。
您甚至可能例如采用其创建的堆栈跟踪,并使用它作为线程名。这听起来很奇怪,但是这取决于你的代码是如何组织它可以用于调试目的非常有用的。
这似乎是一件小事,但是,你必须有很多的线程一下子的事情“已经停止”(无论是对可能在网络协议的一大败笔,因为僵局的原因还是一个非常复杂的应用程序,这将不那么明显 - 或其他无尽的原因),然后让从Java堆栈转储,所有的线程被称为“线程1”,“线程2”,“线程-3”并不总是非常有用的(这取决于如何你的线程结构以及是否可以有效地分不清哪个是哪个只是他们的堆栈跟踪 - 如果你正在使用并不总是能够多线程组中的所有运行相同的代码)
说了这么多,你当然也可以做一个通用的方法上面通过创建线程类,设置它的名字为其创建调用堆栈跟踪的扩展,然后使用与您的Runnable的实现,而不是标准Java线程类(见下文),但除了在堆栈跟踪有可能是将是有益的线程名进行调试(参考许多队列或它例如可以处理插座多一个上下文中的具体信息在这种情况下你可能更喜欢专门为这种情况下,扩展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]
可运行的,因为:
- 留下更多的灵活性 可运行的实施延长 另一类
- 分隔的代码 的执行
- 可以让你跑你 从可运行的一个线程池, 事件线,或在任何其他方式在 未来。
甚至如果你不需要任何此,现在,你可能在未来。由于没有受益于复盖线,可运行的是一个更好的解决方案。
由于这是一个非常受欢迎的主题和良好的答案都是遍布全和处理的非常深入,我觉得这是合理的汇编的很好的答案,从其他人成为一个更简洁的形式,这样新来的人有一个简单的概述前期:
你通常延长一级的添加或修改的功能。所以, 如果你不想 要 复盖 任何 线的行为, 然后使用可运行.
在同样的光, 如果你不需要 要 继承 线的方法,你可以不用, 开销 通过使用可运行.
单一的继承:如果延长线你不能从任何其他类,所以如果这就是你需要做什么,你必须使用可运行.
它是好的设计为单独领域逻辑,从技术手段,在这个意义上,它是最好有一个可运行的任务 隔离 你的 任务 你的 亚军.
你可以 执行 同样运行的 象多次, 线的对象,然而,只可以开始一次。(可能的原因,为什么遗嘱执行人接受Runnable,但不是线。)
如果你发展你的任务,因为可运行的,你有 所有的灵活性,如何使用它现在和未来.你可以拥有它同时运行,通过执行者,但也通过线。而且你还也可以使用/叫它非同时在同一线只是因为任何其他普通类型的对象。
这使得它也更容易 单独的 任务的逻辑和并发 面 你的 单元测试.
如果你有兴趣在这个问题,你可能会也有兴趣的 之间的差异可调用和可运行的.
这是在Oracle的定义和启动一个Thread 教程:
您应该使用哪种这些成语吗?第一成语,其采用 Runnable对象,更是一般,因为Runnable对象可以 继承比其他线程的一类。第二个成语更容易使用 在简单的应用程序,而是由事实,你的任务限制 类必须是线程的后代。本课的重点是第一 方法,它从Thread对象分开Runnable任务 执行任务。这不仅是方法更灵活,但 它适用于覆盖的高层次的线程管理API 后面。
换句话说,实施Runnable
将在你的类扩展比Thread
其它一类方案工作。 Java不支持多重继承。此外,使用一些高层次的线程管理API时,延长Thread
将是不可能的。其中,延长Thread
最好的唯一的方案是在一个小的应用程序不会受到在将来的更新。它几乎总是更好地执行Runnable
,因为它是你的项目的发展更加灵活。一个设计变化不会有大的影响,你可以在Java中实现多个接口,但只能继承一个类。
如果我没看错,这是或多或少类似于
延伸建立 “的是强>” 的关系&接口提供 “的有一个强>” 的能力。
体型的 实现Runnable 强>:
- 如果您还没有扩展Thread类,并修改线程API默认实现
- 如果您正在执行消防和忘记命令
- 如果您已经扩展了另一个类 醇>
- 如果您有任何覆盖这些主题方法如Oracle文档页中列出 醇>
体型 “的 延伸螺纹 强>”:
通常你并不需要重写线程的行为。这样的实现Runnable 强>是优选的大部分时间。
在不同的音符,使用先进ExecutorService
或ThreadPoolExecutorService
API提供了更多的灵活性和控制。
看一看这个SE问题:
最简单的解释将是通过实施Runnable
我们可以在相同的对象分配给多个线程,并且每个Thread
共享相同的对象状态和行为。
例如,假设有两个线程,的线程1 强>提出的整数数组中的和线程2 强>从阵列采用整数当阵列被充满。请注意,为了使的线程2 以工作,它需要知道数组的状态下,是否的线程1 强>填补它与否。
实现Runnable
让你有这样的灵活性,而extends Thread
让你为每个线程因此由线程1做任何更新输给线程2创建新的对象共享的对象。
从可运行执行分离Thread类也避免螺纹和run()方法之间的潜在的同步问题。一个单独的可运行通常得到的方式更大的灵活性,可运行代码被引用并执行。
一个 螺纹 体现了 运行上下文 (如在执行上下文:堆框架,thread id,等等。) 的 异步执行 一块代码。那 一块代码 理想的情况应同执行,是否 同步 或 异步.
如果你捆绑在一起的一个实,你所得到的对象两个 无关 原因导致的变化:
- 线处理在应用程序(ie。查询和修改执行上下文)
- 算法的实施代码(可运行部分)
如果您使用的语言支持分类或多个继承,然后你可以每隔离的原因在自己的超一流的,但它归结为一样构成的两个对象,因为它们的功能设置不会重叠。这就是为理论。
在实践中,一般来说,一方案不需要进行更多的复杂性超过实际需要。如果你有一个线上工作的具体任务,而没有改变这项任务,有可能是没有点制作的任务单独的课程,和你的代码仍然是更简单。
在该上下文中 Java, ,因为该设施 已经有, 它是可能更容易开始直接与独立 Runnable
课程,并通过他们的实例 Thread
(或 Executor
)的实例。一旦 使用 以这一模式,这不是难以使用(或甚至读)的比简单的可运行的线的情况。
你想实现一个接口,而不是扩展一个基类的一个原因是你已经扩展了一些其他类。只能扩展一个类,但可以实现任意数量的接口。
如果您延长线,你基本上防止你的逻辑被其他线程不是“本”执行。如果你只想要的一些的线程来执行你的逻辑,最好是刚刚实现Runnable。
如果您使用可运行的,你可以节省空间扩展到您的任何其他类。
我们能再次访问,我们希望我们的类表现为Thread
的根本原因?
没有任何理由可言,我们只是想执行一个任务,最有可能在异步模式下,这恰恰意味着任务的执行必须从我们的主线程和主线程跳转如果完成年初,可能会或可能不会等待对于分支通路(任务)。
如果这是整个目的,那么我在哪里看到一个专门的线程的需要。这可以通过拿起从系统的线程池中的原纱,并为其分配我们的任务(可能是我们的类的实例)来完成,就是这样。
因此,让我们听从OOPS概念,并写一个类,我们需要的类型。有很多方法可以做到的事情,用正确的方式做事情了。
我们需要一个任务,所以写它可以在一个线程中运行任务的定义。因此,使用可运行。
切记implements
是专门用于赋予行为和extends
用于赋予功能/属性。
我们不希望线程的财产,而不是我们希望我们的类表现为可运行的任务。
是, 如果你打电话的ThreadA呼叫,则不需要调用start方法和运行方法仅仅是ThreadA中一流的呼叫后调用。 但是,如果使用ThreadB呼叫,则需要在必要的启动线程呼叫run方法。 如果您有任何更多的帮助,回答我。
我觉得这是最有效的使用Runnable接口所提到的所有原因,但有时我想扩展Thread这样我就可以创建自己的线程停止方法和直接调用它,我已创建的线程上。
Java不支持多重的传承,所以如果你继承Thread类,那么任何其他类别将被延长。
例如:如果创建一个applet然后它必须所以这里延伸Applet类创建线程的唯一方式是通过实现Runnable接口
Runnable
是一个接口,而Thread
是实现该接口的类。从设计的角度来看,应该有之间的任务是如何定义的,它是如何执行的完全分离。前者是Runnalbe
责任落实,而后者是Thread
类的工作。在大多数的实施Runnable
的情况下,是按照正确的方式。
主题和可运行之间的差异 。如果我们使用Thread类,然后线程等于我们创建的对象的数量的数来创建线程。 如果我们通过实现可运行接口创建线程那么我们可以用单个对象创建多个thread.So单个对象被多个Thread.So将花费较少的内存共享
所以,这取决于需求,如果我们的数据是不是灵敏。所以它可以多个Thread之间共享,我们可以使用Runnable接口。
加入我两分钱在这里
总是尽可能使用 implements Runnable
.下面是两个警告你为什么不应使用
extends Thread
s
最理想的是你永远不应该延长线的类别;的
Thread
类应该做的final
.至少它的方法thread.getId()
.看看 此 讨论一个错误有关的延伸Thread
s.那些想要解决的难题可以看到的另一个副作用的延伸线。以下代码 将印无法访问的代码时,没有人通知他们。
请看看 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 ");
}
}
}