Java中的“实现Runnable”与“扩展线程”

时间:2009-02-12 14:28:46

标签: java multithreading runnable implements java-threads

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

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

43 个答案:

答案 0 :(得分:1553)

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

实用术语中,这意味着您可以实现Runnable并从其他类扩展。

答案 1 :(得分:519)

tl; dr:implements Runnable更好。但是,需要注意的是

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

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

跟进:有一个FutureTask constructor允许您使用Runnables(如果这是您最熟悉的)并且仍然可以获得现代并发工具的好处。引用javadoc:

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

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

因此,如果我们将runnable替换为您的threadA,我们会收到以下信息:

new FutureTask<Object>(threadA, null)

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

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

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

答案 2 :(得分:242)

故事的道德:

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

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

继承少,界面更多。

答案 3 :(得分:204)

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

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

  1. 当您扩展Thread类时,之后您无法扩展您需要的任何其他类。 (如您所知,Java不允许继承多个类。)
  2. 当您实现Runnable时,您可以为您的班级节省空间,以便将来或现在扩展任何其他课程。
  3. 但是,实施Runnable和扩展线程之间的一个显着差异 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接口时扩展另一个类来创建一个线程。

    我希望这会有所帮助!

答案 4 :(得分:75)

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

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

答案 5 :(得分:69)

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

最常见的区别是

enter image description here

当你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时,它将同一个对象共享给多个线程。

答案 6 :(得分:65)

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

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

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

<强>可运行:
实现interface Runnable时,意味着您在另一个线程中创建了run able的内容。现在创建可以在线程内运行的东西(在线程内部可运行)并不意味着创建一个Thread 因此,类MyRunnable只不过是具有void run方法的普通类。 它的对象是一些普通的对象,只有一个方法run,它会在被调用时正常执行。 (除非我们在一个线程中传递对象)。

<强>发
class Thread,我想说一个非常特殊的类,它能够启动一个新的Thread,它实际上可以通过start()方法启用多线程。

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

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

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

从技术上和理论上讲,这两者都是启动线程所必需的,一个将运行一个使其运行(如 Wheel and Engine 的汽车)。

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

可以仅使用class Thread创建和运行线程,因为类Thread实现Runnable所以我们都知道Thread里面是Runnable

最后,ThreadRunnable是多线程而非竞争对手或替代者的补充。

答案 7 :(得分:42)

您应该实现Runnable,但如果您使用的是Java 5或更高版本,则不应该使用new Thread启动它,而应使用ExecutorService。有关详细信息,请参阅:How to implement simple threading in Java

答案 8 :(得分:32)

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

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

答案 9 :(得分:19)

我想说还有第三种方式:

public class Something {

    public void justAnotherMethod() { ... }

}

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

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

答案 10 :(得分:16)

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

Runnablefunctional interface,这意味着可以使用lambda表达式或方法引用创建它的实例。

您的示例可以替换为:

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

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

executor.execute(runner::run)

这些不仅比您的示例短得多,而且还具有使用Runnable优于Thread的其他答案中所述的许多优势,例如单一责任和使用构图,因为您&#39 ;不专门研究线程的行为。如果你需要的只是Runnable,那么这种方式也可以避免创建一个额外的类。

答案 11 :(得分:15)

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

答案 12 :(得分:11)

这里的每个人似乎都认为实现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]

答案 13 :(得分:10)

Runnable因为:

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

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

答案 14 :(得分:10)

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

  1. 您通常会扩展一个类来添加或修改功能。因此,如果您不希望 覆盖任何线程行为,请使用Runnable。

  2. 同样,如果您不需要 继承线程方法,那么您可以不用开销使用Runnable。

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

  4. 将域逻辑与技术手段区分开来是一个很好的设计,从这个意义上来说,最好让隔离 任务 >转轮

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

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

  7. 这使单元测试 分离任务逻辑和并发方面更容易 < / LI>

  8. 如果您对此问题感兴趣,可能也对difference between Callable and Runnable感兴趣。

答案 15 :(得分:8)

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

enter image description here

答案 16 :(得分:8)

这在Oracle的Defining and Starting a Thread教程中讨论过:

  

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

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

答案 17 :(得分:6)

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

What is the difference between an interface and abstract class?

extends establish&#34; 是A &#34;关系&amp;界面提供&#34; 有一个&#34;能力。

首选 实现Runnable

  1. 如果您不必扩展Thread类并修改Thread API默认实现
  2. 如果您正在执行火灾并忘记命令
  3. 如果您已经在扩展另一个班级
  4. 首选&#34; 扩展线程 &#34; :

    1. 如果必须覆盖oracle文档页面中列出的任何Thread方法
    2. 通常,您不需要覆盖线程行为。所以实现Runnable 在大多数情况下都是首选。

      另外,使用高级ExecutorServiceThreadPoolExecutorService API可提供更多灵活性和控制权。

      看看这个SE问题:

      ExecutorService vs Casual Thread Spawner

答案 18 :(得分:6)

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

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

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

答案 19 :(得分:5)

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

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

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

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

始终记住implements专门用于传授行为,extends用于传授功能/属性。

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

答案 20 :(得分:5)

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

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

答案 21 :(得分:5)

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

答案 22 :(得分:5)

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

答案 23 :(得分:4)

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

答案 24 :(得分:4)

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

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

/**
 * 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 here
                Thread.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();
    }

}

答案 25 :(得分:4)

是, 如果调用ThreadA调用,那么在调用ThreadA类之后不需要调用start方法和run方法调用。 但是如果使用ThreadB调用则需要为调用run方法启动线程。 如果您有任何帮助,请回复我。

答案 26 :(得分:4)

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

例如:如果你创建了一个applet,那么它必须扩展Applet类,所以这里创建线程的唯一方法是实现Runnable接口

答案 27 :(得分:4)

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

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

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

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

答案 28 :(得分:4)

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

  1. 理想情况下,您永远不应该扩展Thread类; Thread课程应该final。 至少它的方法如thread.getId()。 有关扩展Thread的错误,请参阅this讨论。

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

  3. 请参阅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 ");
            }
        } 
    }
    

答案 29 :(得分:4)

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

因此,如果我们的数据不敏感,则视需求而定。所以它可以在多个Thread之间共享,我们可以使用Runnable接口。

答案 30 :(得分:4)

这是SSOLID:单一责任。

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

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

  1. 应用程序中的线程处理(即查询和修改执行上下文)
  2. 由一段代码(可运行部分)实现的算法
  3. 如果您使用的语言支持部分类或多重继承,那么您可以在其自己的超类中隔离每个原因,但它归结为与组合这两个对象相同,因为它们的功能集不重叠。那就是理论。

    实际上,一般来说,程序不需要携带比必要更复杂的程序。如果您有一个线程处理特定任务,而没有更改该任务,则可能没有必要将任务分开,并且您的代码仍然更简单。

    Java 的上下文中,由于工具已经存在,因此可能更容易直接使用独立的Runnable类,并通过他们的实例到Thread(或Executor)实例。一旦使用到该模式,它就不会比简单的可运行线程情况更难使用(甚至读取)。

答案 31 :(得分:3)

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

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

但是如果你的子类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() {
            @Override
            public 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

答案 32 :(得分:3)

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

答案 33 :(得分:2)

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

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

答案 34 :(得分:2)

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

答案 35 :(得分:2)

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

  

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

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

答案 36 :(得分:1)

简单的说法是: 如果你实现接口意味着你正在实现它的所有方法,并且如果你扩展类,你继承你选择的方法...... 在这种情况下,只有一个名为Run()的方法,因此更好地实现Runnable接口..

答案 37 :(得分:1)

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

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

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

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

答案 38 :(得分:1)

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]

我认为已经对该主题进行了很多讨论。 尽管这可能对基础知识很有帮助。

答案 39 :(得分:0)

我会说实际的任务是与线程分离的。在Runnable的情况下,我们可以将任务传递给Thread,Executor框架等,而通过扩展Thread任务则与线程对象本身耦合。如果扩展Thread,则无法完成任务隔离。就像我们将任务烧写到Thread对象一样,就像IC芯片一样(更具体地说,它将无法处理任何任务)。

答案 40 :(得分:0)

Thread和Runnable之间的主要区别是: -线程就像:工作者(执行Runnable) -Runnable就像:Job(由Thread执行)

答案 41 :(得分:0)

<块引用> <块引用> <块引用> <块引用>

2021 年答复

两者都是一样的。但建议实施 runnable。因为我们也可以扩展另一个类。通过使用这种方式,您需要创建一个实际的线程。但是通过扩展线程类,您可以单独创建您的类。

答案 42 :(得分:-1)

您可以联合使用

示例:

public class A implements Runnable{

    @Override
    public void run() {


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

    }

}



public class Test {

    public static void main(String[] args) {

        Thread myThread =new Thread(new A());// 1
        myThread.start();
        System.out.println(" executed after thread A");//will never be reached

    }

}