java - runnable用法 - thread runnable区别




“实现Runnable”与“扩展线程” (20)

从我在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. Java不支持多继承,这意味着您只能扩展一个Java类,所以一旦扩展了Thread类,您就失去了机会,无法扩展或继承Java中的另一个类。
  2. 在面向对象的编程中,扩展类通常意味着增加新的功能,修改或改进行为。 如果我们没有对Thread进行任何修改,而是使用Runnable接口。
  3. Runnable接口表示一个Task ,可以通过纯ThreadExecutors或任何其他方式执行。 因此,与Runnable相比, Task逻辑分离是很好的设计决策。
  4. 将任务分离为Runnable意味着我们可以重用任务,并且可以通过不同方式执行任务。 由于一旦完成,你无法重新启动一个Thread ,再次RunnableThread的任务, Runnable是赢家。
  5. Java designer recognizes this and that's why Executors accept Runnable as Task and they have worker thread which executes those task.
  6. Inheriting all Thread methods are additional overhead just for representing a Task which can can be done easily with Runnable .

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

一般来说,我会推荐使用类似于Runnable东西而不是Thread因为它可以让你的工作只与你的并发选择松散结合。 例如,如果您使用Runnable并稍后决定这实际上不需要它自己的Thread ,则可以调用threadA.run()。

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

后续:有一个FutureTask构造函数 ,允许您使用Runnables(如果这是您最熟悉的),并且仍然可以从现代并发工具中受益。 引用javadoc:

如果您不需要特定的结果,请考虑使用表单的结构:

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

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

new FutureTask<Object>(threadA, null)

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

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

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

Can we re-visit the basic reason we wanted our class to behave as a Thread ? There is no reason at all, we just wanted to execute a task, most likely in an asynchronous mode, which precisely means that the execution of the task must branch from our main thread and the main thread if finishes early, may or may not wait for the branched path(task).

If this is the whole purpose, then where do I see the need of a specialized Thread. This can be accomplished by picking up a RAW Thread from the System's Thread Pool and assigning it our task (may be an instance of our class) and that is it.

So let us obey the OOPs concept and write a class of the type we need. There are many ways to do things, doing it in the right way matters.

We need a task, so write a task definition which can be run on a Thread. So use Runnable.

Always remember implements is specially used to impart a behaviour and extends is used to impart a feature/property.

We do not want the thread's property, instead we want our class to behave as a task which can be run.


Difference between Extending Thread and Implementing Runnable are:


I find it is most useful to use Runnable for all the reasons mentioned, but sometimes I like to extend Thread so I can create my own thread stopping method and call it directly on the thread I have created.


If I am not wrong, it's more or less similar to

界面和抽象类有什么区别?

extends establishes " Is A " relation & interface provides " Has a " capability.

Prefer implements Runnable :

  1. If you don't have to extend Thread class and modify Thread API default implementation
  2. If you are executing a fire and forget command
  3. If You are already extending another class

Prefer " extends Thread " :

  1. If you have to override any of these Thread methods as listed in oracle documentation page

Generally you don't need to override Thread behaviour. So implements Runnable is preferred for most of the times.

On a different note, using advanced ExecutorService or ThreadPoolExecutorService API provides more flexibility and control.

Have a look at this SE Question:

ExecutorService vs Casual Thread Spawner


One difference between implementing Runnable and extending Thread is that 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.

A class that implements Runnable is not a thread and just a class. For a Runnable to be executed by a Thread, you need to create an instance of Thread and pass the Runnable instance in as the target.

In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

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


One reason you'd want to implement an interface rather than extend a base class is that you are already extending some other class. You can only extend one class, but you can implement any number of interfaces.

If you extend Thread, you're basically preventing your logic to be executed by any other thread than 'this'. If you only want some thread to execute your logic, it's better to just implement Runnable.


That's the S of SOLID : Single responsibility.

A thread embodies the running context (as in execution context: stack frame, thread id, etc.) of the asynchronous execution of a piece of code. That piece of code ideally should be the same implementation, whether synchronous or asynchronous .

If you bundle them together in one implementation, you give the resulting object two unrelated causes of change:

  1. thread handling in your application (ie. querying and modifying the execution context)
  2. algorithm implemented by the piece of code (the runnable part)

If the language you use supports partial classes or multiple inheritance, then you can segregate each cause in its own super class, but it boils down to the same as composing the two objects, since their feature sets don't overlap. That's for the theory.

In practice, generally speaking, a programme does not need to carry more complexity than necessary. If you have one thread working on a specific task, without ever changing that task, there is probably no point in making the tasks separate classes, and your code remains simpler.

In the context of Java , since the facility is already there , it is probably easier to start directly with stand alone Runnable classes, and pass their instances to Thread (or Executor ) instances. Once used to that pattern, it is not harder to use (or even read) than the simple runnable thread case.


This is discussed in Oracle's Defining and Starting a Thread tutorial:

Which of these idioms should you use? The first idiom, which employs a Runnable object, is more general, because the Runnable object can subclass a class other than Thread. The second idiom is easier to use in simple applications, but is limited by the fact that your task class must be a descendant of Thread. This lesson focuses on the first approach, which separates the Runnable task from the Thread object that executes the task. Not only is this approach more flexible, but it is applicable to the high-level thread management APIs covered later.

In other words, implementing Runnable will work in scenarios where your class extends a class other than Thread . Java does not support multiple inheritance. Also, extending Thread will not be possible when using some of the high-level thread management APIs. The only scenario where extending Thread is preferable is in a small application that won't be subject to updates in future. It is almost always better to implement Runnable as it is more flexible as your project grows. A design change won't have a major impact as you can implement many interfaces in java, but only extend one class.


if you use runnable you can save the space to extend to any of your other class.


因为:可运行

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

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


实例化接口给出了代码和线程实现之间更清晰的分离,所以我宁愿在这种情况下实现Runnable。


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

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

我会说,多线程只有两个步骤。 让我指出我的观点。

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

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

为什么不明智比较?
因为我们需要这两者来进行多线程。

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

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

所以技术上和理论上他们都需要启动一个线程,一个会运行 ,一个会运行 (像机动车辆的Wheel and Engine )。

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

但是可以仅使用class Thread创建和运行class Thread因为Class Thread实现了Runnable所以我们都知道Thread也是一个Runnable里面。

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


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

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


我会说有第三种方式:

public class Something {

    public void justAnotherMethod() { ... }

}

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

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


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

实际上 ,这意味着您可以实现Runnable并从另一个类扩展。


有一件我感到惊讶的事情还没有被提及,那就是实现Runnable可以让你的类更灵活。

如果你扩展线程,那么你正在做的动作总是会在一个线程中。 但是,如果您实现Runnable ,则不必这样做。 您可以在一个线程中运行它,或者将它传递给某种执行程序服务,或者将它作为单个线程应用程序中的任务传递(也许稍后运行,但在同一个线程中)。 如果你只是使用Runnable ,那么这些选项比你将自己绑定到Thread更加开放。


这里的每个人似乎都认为实现Runnable是一条路,我并不认同他们,但在我看来,扩展Thread也是一种情况,实际上你已经在代码中展示了它。

如果你实现了Runnable,那么实现Runnable的类不能控制线程名称,它是可以设置线程名称的调用代码,如下所示:

new Thread(myRunnable,"WhateverNameiFeelLike");

但是如果你扩展了Thread,那么你可以在类本身中进行管理(就像在你的例子中,你将线程命名为ThreadB)。 在这种情况下你:

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

B)强迫这个名字被用于该类的所有实例(除非你忽略了它是一个线程,并且像上面这样做,就像它是一个Runnable一样,但是我们在这里讨论的是约定,所以可以忽略我感觉到的可能性)。

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

这可能看起来像一个小事情,但是如果你有一个非常复杂的应用程序,并且有很多线程,并且所有突然的事情都会“停止”(无论是出于死锁的原因还是可能因为网络协议中的缺陷明显的 - 或其他无休止的原因),然后从所有线程被称为“线程-1”,“线程-2”,“线程-3”的Java获得堆栈转储并不总是非常有用(这取决于你的线程是如何结构化以及是否可以有用地告诉哪些是由他们的堆栈跟踪而来 - 如果您使用全部运行相同代码的多个线程组,则并非总是可行)。

话虽如此,你当然也可以通过创建一个线程类的扩展来以通用的方式完成上述操作,该类将其名称设置为创建调用的堆栈跟踪,然后将其用于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]

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

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

  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 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接口的同时扩展另一个类来创建线程。

我希望这个能帮上忙!





java-threads