从我在Java中使用线程的时间来看,我发现了两种编写线程的方法:

使用可运行的机具:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

或者,使用扩展线程:

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

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


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

编辑:这原本是说“实现一个接口需要更少的资源”,但无论如何都需要创建一个新的线程实例,所以这是错误的。


是的:implements Runnable是首选的实现方式,IMO。你并没有真正专门研究线程的行为。你只是在给它一些东西。这意味着构图是哲学上“更纯粹”的方式。

实际上,这意味着您可以实现Runnable并从另一个类扩展。。。您还可以通过lambda表达式实现Runnable,如Java 8。


实例化接口在代码和线程的实现之间提供了更清晰的分离,因此在这种情况下我更希望实现Runnable。


tl;dr:implements Runnable更好。然而,警告很重要。

一般来说,我建议使用Runnable而不是Thread这样的工具,因为它允许您保持工作与并发选择之间的松散耦合。例如,如果您使用了一个Runnable,并且稍后决定它实际上不需要自己的Thread,那么您可以调用threadA.run()。

注意:在这里,我强烈反对使用原始线程。我更喜欢使用Callables和FutureTasks(来自javadoc:“可取消的异步计算”)。现代并发支持的超时、适当取消和线程池的集成对我来说都比成堆的原始线程有用得多。

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

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

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

因此,如果我们用threadA替换它们的runable,我们会得到以下结果:

new FutureTask<Object>(threadA, null)

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

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

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

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


有一件事我很惊讶,还没有提到,那就是实现Runnable使您的类更加灵活。

如果你扩展了线程,那么你正在做的动作总是在一个线程中。然而,如果你实现了Runnable,那就不必了。你可以在一个线程中运行它,或者将它传递给某种类型的执行器服务,或者将其作为一个任务在一个单线程应用程序中传递(可能稍后运行,但在同一个线程内)。如果只使用Runnable,则选项比将自己绑定到Thread时开放得多。


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


故事的寓意:

仅当您想要覆盖某些行为时才继承。

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

继承更少,接口更多。


可运行,因为:

为可运行的实现以扩展另一类将代码与处决允许您运行可从线程池运行事件线程,或以任何其他方式未来。

即使你现在不需要这些,你将来也可以。由于重写线程没有好处,Runnable是一个更好的解决方案。


我想说还有第三种方法:

public class Something {

    public void justAnotherMethod() { ... }

}

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

也许这受到了我最近大量使用Javascript和Actionscript 3的影响,但是这样你的类就不需要实现一个非常模糊的接口,比如Runnable。


对如果调用ThreadA调用,则不需要调用start方法,run方法只在调用ThreadA类后调用。但如果使用ThreadB调用,则需要启动线程来调用run方法。如果你还有什么帮助,请回复我。


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


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

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


这里的每个人似乎都认为实现Runnable是一条路,我并不真的不同意他们的观点,但在我看来,扩展Thread也是有道理的,事实上,您已经在代码中演示了这一点。

如果您实现Runnable,那么实现Runnable的类对线程名称没有控制权,调用代码可以设置线程名称,如下所示:

new Thread(myRunnable,"WhateverNameiFeelLike");

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

A) 可能会为调试目的提供一个更有用的名称

B) 强制将该名称用于该类的所有实例(除非您忽略了它是一个线程的事实,并将其作为Runnable来执行上述操作,但我们在这里讨论的是约定,所以可以忽略我认为的可能性)。

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

这看起来可能是一件小事,但如果你有一个非常复杂的应用程序,其中有很多线程,并且突然“停止”了(可能是因为死锁,也可能是因为网络协议中的一个不太明显的缺陷,或者其他无休止的原因),然后从Java获取堆栈转储,其中所有线程都被称为“线程-1”、“线程-2”、,“Thread-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]

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

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

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


这就是SOLID的s:单一责任。

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

如果将它们捆绑在一个实现中,则会给结果对象两个不相关的更改原因:

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

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

在实践中,一般来说,一个方案不需要比必要的更复杂。如果您有一个线程在处理一个特定的任务,而不需要更改该任务,那么将任务划分为单独的类可能没有任何意义,并且代码仍然更简单。

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


如果您希望实现或扩展任何其他类,那么Runnable接口是最可取的,否则,如果您不希望任何其他类扩展或实现,那么Thread类是最好的。

最常见的区别是

当您扩展Thread类时,在此之后,您不能扩展所需的任何其他类。(如您所知,Java不允许继承多个类)。

当您实现Runnable时,您可以为您的类节省空间,以便将来或现在扩展任何其他类。

Java不支持多个继承,这意味着您只能在Java中扩展一个类,因此一旦扩展了Thread类,您就失去了机会,无法在Java中延伸或继承另一个类。在面向对象编程中,扩展类通常意味着添加新功能,以及修改或改进行为。如果我们没有对线程进行任何修改,那么请改用Runnable接口。可运行接口表示一个任务,它可以由普通线程或执行器或任何其他方式执行。因此,将Task逻辑分离为Runnable而不是Thread是一个很好的设计决策。将任务分离为可运行任务意味着我们可以重用任务,也可以通过不同的方式执行任务。因为一旦线程完成,就不能重新启动它。任务的Runnable与Thread,Runnable是赢家。Java设计器认识到这一点,这就是为什么Executors接受Runnable作为Task,并且他们有执行这些任务的工作线程。继承所有线程方法只是表示一个任务的额外开销,这可以通过Runnable轻松完成。

由javareviewed.blogspot.com提供

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

然而,显著的区别是。

当您扩展Thread类时,每个线程都会创建一个唯一的对象并与其关联。当您实现Runnable时,它将同一对象共享给多个线程。


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

如果扩展Thread,基本上就是防止逻辑被除“this”之外的任何其他线程执行。如果您只希望一些线程执行您的逻辑,那么最好只实现Runnable。


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


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

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

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

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

始终记住,机具专门用于传递行为,扩展用于传递特性/属性。

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


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

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

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


由于这是一个非常流行的话题,而且好的答案到处都是,而且处理得非常深入,所以我觉得将其他人的好答案汇编成更简洁的形式是合理的,因此新来者可以很容易地概括:

通常扩展类以添加或修改功能。因此,如果不想覆盖任何线程行为,请使用Runnable。同样,如果不需要继承线程方法,那么可以通过使用Runnable来避免这种开销。单一继承:如果您扩展Thread,则不能从任何其他类扩展,因此如果这是您需要做的,则必须使用Runnable。将领域逻辑与技术手段分开是一个很好的设计,从这个意义上说,最好有一个可运行的任务将您的任务与运行者隔离开来。您可以多次执行同一个Runnable对象,但Thread对象只能启动一次。(也许是执行器接受Runnables而不接受Threads的原因。)如果您将任务开发为Runnable,那么您现在和将来都可以灵活使用它。您可以通过执行器同时运行,也可以通过线程运行。您还可以在同一线程中非并发地使用/调用它,就像其他任何普通类型/对象一样。这也使得在单元测试中分离任务逻辑和并发方面更加容易。如果你对这个问题感兴趣,你可能也会对Callable和Runnable之间的区别感兴趣。


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

因此,如果我们的数据不敏感,则取决于要求。所以它可以在多个线程之间共享,我们可以使用Runnable接口。


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


随着Java8的发布,现在有了第三个选项。

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

您的示例可以替换为:

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

或者如果要使用ExecutorService和方法引用:

executor.execute(runner::run)

这些不仅比示例要短得多,而且还具有其他答案中所述的使用Runnable over Thread的许多优点,例如单一责任和使用组合,因为您没有专门处理线程的行为。如果您只需要一个Runnable,那么这种方法也可以避免创建一个额外的类。


在这里加上我的两分钱-始终尽可能使用机具Runnable。以下是关于为什么不应该使用扩展线程

理想情况下,您不应该扩展Thread类;线程类应为最终类。至少它的方法像thread.getId()。有关扩展线程的错误,请参阅本讨论。那些喜欢解谜的人可以看到扩展线程的另一个副作用。以下代码将在无人通知时打印无法访问的代码。

请参见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 ");
        }
    } 
}

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

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

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

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


实际上,将Runnable和Thread相互比较是不明智的。

这两者在多线程中具有相关性和关系,就像机动车的车轮和发动机关系一样。

我想说,多线程只有一种方法,有两个步骤。让我表明我的观点。

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

线程:class线程,我可以说是一个非常特殊的类,它能够启动一个新的线程,这实际上通过它的start()方法实现了多线程。

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

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

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

因此,从技术上和理论上来说,这两个都是启动线程所必需的,一个线程将运行,另一个线程会使线程运行(就像机动车的车轮和发动机)。

这就是为什么不能使用MyRunnable启动线程,需要将其传递给thread的实例。

但是,只有使用类thread才能创建和运行线程,因为类thread实现了Runnable,所以我们都知道thread内部也是一个Runnable。

最后,Thread和Runnable是多线程的补充,而不是竞争对手或替代者。


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

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

但是,如果您的子类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

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

例如,假设有两个线程,thread1在数组中放入一个整数,thread2在数组填满时从数组中取出整数。请注意,为了让thread2正常工作,它需要知道数组的状态,不管thread1是否已将其填满。

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


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

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

extends建立“Is A”关系,接口提供“Has A”功能。

首选工具可运行:

如果不需要扩展Thread类并修改Thread API默认实现如果你执行的是一个火灾和忘记命令如果您已经在扩展另一个类

首选“扩展线程”:

如果必须重写oracle文档页中列出的任何线程方法

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

另一方面,使用高级ExecutorService或ThreadPoolExecutorServiceAPI提供了更多的灵活性和控制。

看看这个SE问题:

ExecutorService vs休闲线程生成器


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

我刚刚发布了一个示例,因此我也将与您分享:

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

}

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


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

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

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


扩展线程与实现可运行线程之间的区别在于:


线程类定义了几个可由扩展类重写的方法。但要创建线程,我们必须重写run()方法。这同样适用于Runnable。

然而,Runnable是创建线程的首选方法。主要原因如下:

由于Runnable是一个接口,因此可以扩展其他类。但如果您扩展“线程”,则该选项将消失。如果您没有修改或增强大量Thread功能,那么扩展Thread类不是首选方法。


您可以共同使用它们

例子:

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

    }

} 

我会说实际任务与线程分离。在Runnable的情况下,我们可以将任务传递给线程、执行器框架等,而通过扩展Thread任务则与线程对象本身耦合。在扩展线程的情况下,无法执行任务隔离。这就像我们将任务烧成线程对象,就像IC芯片一样(更具体地说,不会得到任务的任何句柄)。


Thread和Runnable的主要区别在于:-线程类似于:Worker(执行Runnable)-可运行的类似于:作业(由线程执行)


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]

我认为已经有很多关于这个话题的讨论,认为这可能对基础知识有所帮助。


如果你想分开你的关注点,比如,如果你想创建任务,而谁是处理你任务的工作人员对你来说无关紧要,那么使用Runnable并将你的工作定义为任务。就像使用executor服务一样,您以Runnable/callable的形式定义任务,并将它们交给executor,然后由executor负责。

如果您想自己创建一个工作线程并将任务分配给该工作线程,那么您需要对工作线程进行更多的控制,然后使用thread类。