Thread start()和Runnable run()有什么区别?

251

假设我们有这两个Runnables:

class R1 implements Runnable {
    public void run() { … }
    …
}

class R2 implements Runnable {
    public void run() { … }
    …
}

那么这两者有什么区别:

public static void main() {
    R1 r1 = new R1();
    R2 r2 = new R2();

    r1.run();
    r2.run();
}

还有这个:

public static void main() {
    R1 r1 = new R1();
    R2 r2 = new R2();
    Thread t1 = new Thread(r1);
    Thread t2 = new Thread(r2);

    t1.start();
    t2.start();
}
14个回答

337

第一个例子:没有多线程。两者在单个(现有的)线程中执行。不会创建新的线程。

R1 r1 = new R1();
R2 r2 = new R2();

r1r2只是实现了Runnable接口并因此实现了run()方法的两个不同对象。当你调用r1.run()时,它在当前线程中执行。

第二个例子:两个独立的线程。

Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);

t1t2Thread 类的对象。当你调用 t1.start() 时,它会启动一个新线程并在其中内部调用 r1run() 方法来执行它。


7
在调用Thread#start()之前,我能否认为与操作系统线程相关的任何事情都没有发生?它只是一个Java对象。 - JaskeyLam
5
根据文件,这是正确的。检查线程对象初始化代码,它与文件一致。在源代码中,调用 start() 方法会调用本地方法,该方法必须执行操作系统线程相关的内容。 - Bhesh Gurung
4
线程构造函数说明在这里。 线程对象初始化的源代码在这里start() 方法的源代码在这里 - Bhesh Gurung

103

如果您只是直接调用run(),它将在调用线程上执行,就像任何其他方法调用一样。Thread.start()是必需的,以实际创建一个新线程,使可运行对象的run 方法并行执行。


2
在 Hotspot JVM 中,Java 线程和本地线程之间存在直接映射关系。Thread.start() 调用会使线程状态从 new 转移到 Runnable 状态。Runnable 并不意味着线程正在运行。一旦本地线程初始化完成,本地线程将调用 Java 线程中的 run() 方法,使得线程状态从 Runnable 转移到 Running。当线程终止时,所有本地线程和 Java 线程的资源都会被释放。 - overexchange
@overexchange 我在哪里可以找到关于状态改变的资料? - twlkyao

82

区别在于Thread.start()启动了一个线程,该线程调用run()方法,而Runnable.run()仅在当前线程上调用run()方法。


46

区别在于当程序调用 start() 方法时,会创建一个新的线程,并且在新线程中执行 run() 中的代码;而如果直接调用 run() 方法,则不会创建新的线程,run() 中的代码将直接在当前线程中执行。

Java 线程中 start()run() 的另一个区别是,您不能调用两次 start()。一旦启动,第二个 start() 调用将在 Java 中抛出 IllegalStateException 异常,而您可以多次调用 run() 方法,因为它只是一个普通方法。


@Ravindrababu,请不要使用粗体,这并没有帮助。 - user207421

21

实际上,Thread.start() 创建一个新的线程并拥有其自己的执行方案。

Thread.start() 异步调用 run()方法,将新线程的状态改变为 Runnable。

Thread.run() 不创建任何新的线程。相反,它在当前正在运行的线程中同步执行 run 方法。

如果你正在使用 Thread.run(),那么你根本没有使用多线程的特性。


8

run()方法的调用与其他方法一样,在调用线程上执行。而Thread.start()则会创建一个新的线程。 在程序中调用run()方法是一个错误。


7

如果您在主方法中运行run(),则主线程将调用run方法,而不是您要求运行的线程。

start()方法创建新线程,需要执行run()方法。


“main方法”与此无关。 - user207421
3
@EJP,通过“main”,作者指的是调用方法。他的答案相当不错。+1;-) - dom_beau
1
@dom_beau 如果他是这个意思,他应该直接说出来。他所说的是错误的。这个答案没有什么“相当好”的地方,只是一团混乱。 - user207421

6

t.start() 是库提供的方法,用于在代码中调用时创建一个新线程。

r.run() 是你为库提供的方法,在新线程中被调用。


大多数答案都忽略了重点,就是就Java语言而言,t.start()r.run()之间没有更多的区别,就像其他两个方法一样。

它们只是方法。它们都在调用它们的线程中运行。它们都执行编码时的任务,然后它们都返回到调用者所在的同一个线程。

最大的区别是,t.start()的大部分代码是本地代码,而在大多数情况下,r.run()的代码将是纯Java代码。但这并不是很大的区别。代码就是代码。本地代码更难找到,当你找到它时也更难理解,但它仍然只是告诉计算机该做什么的代码。

那么,t.start()做了什么?

它创建一个新的本地线程,安排该线程调用t.run(),然后告诉操作系统让新线程运行。然后它返回。

r.run()做什么?

有趣的是,问这个问题的人就是写r.run()的人。 r.run()执行你(即编写它的开发人员)设计的任务。


4

Thread.start()方法会将线程注册到调度器中,随后调度器会调用run()方法。同时,Thread是一个类,而Runnable是一个接口。


4

成员们提出的观点都是正确的,我只想补充一些内容。问题在于JAVA不支持多重继承。但是如果您想从另一个类A派生一个类B,但是您只能从一个类派生。现在的问题是如何从两个类A和Thread中“派生”。因此,您可以使用Runnable接口。

public class ThreadTest{
   public void method(){
      Thread myThread = new Thread(new B());
      myThread.start;
   }
}

public class B extends A implements Runnable{...

通过一个关于Runnable接口和Thread类的例子,优美地解释了run()方法。 - AndroidLearner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接