需要对Java线程做一些澄清:start()和run()

时间:2015-08-10 17:13:21

标签: java multithreading

根据StackOverflow的答案,start()创建一个线程并在这个新线程中执行代码,run()执行当前线程中的代码而不创建任何代码 - 就好像它是{{1} }}

但是文档,如果我理解正确的话,说Runnable初始化一个新线程,如果你想在同一个线程中重新执行你的代码,你可以调用start()

哪一个是对的?

UPD

好的,看起来手动调用run()并不是一个好习惯,至少没用。是否有一些很好的方法来调用run()中的代码?比如,我经常需要一个相对缓慢的动作,一次一个实例 - 那么呢?

4 个答案:

答案 0 :(得分:6)

如上所述, 他们都不对。

start不会创建或初始化线程。它启动一个线程。来自the documentation

  

使该线程开始执行; Java虚拟机调用此线程的run方法。

线程是创建并通过调用Thread构造函数进行初始化,在调用start之前,您必须自然地执行此操作。 (无论如何,这是“外部神话”;我必须检查JVM规范,但我怀疑JVM可能有效推迟实际创建OS线程,直到/除非你叫start。)

在正常情况下,您永远不会直接调用run实例的Thread方法。相反,您通过start启动线程,JVM在分配给该run实例的实际线程上调度Thread。如果直接调用run,则会在您使用的线程上调用它,这可能不是分配给Thread实例的线程。

您可能会发现Java concurrency tutorial有用。

重新评论以下内容:

  

如何重新执行run()中的代码?重新启动线程或者有一个控制执行的boolean值的无限循环?

后者,但不是无限循环,具有终止条件的循环。来自start文档:

  

不止一次启动线程永远不合法。特别是,一旦完成执行,线程可能无法重新启动。

所以一旦run返回,那就是它,你不能继续使用该线程。相反,您的线程不会从run返回,直到其工作完成。如果你想保持它等待工作,它应该等待某种信号量,你可以从线程外发出信号,唤醒它做一些工作。

上述教程的一个小节讨论了使用waitnotify / notifyAllGuarded Blocks进行此操作的一种方法。

这是一个简单的例子,每次调用System.out.println都会打印调用线程的名称,这样你就可以看到哪个线程做了什么:

Live Copy

class Example
{
    public static void main (String[] args) throws java.lang.Exception
    {
        Thread t= new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + ": Running run");
            }
        });
        System.out.println(Thread.currentThread().getName() + ": Running main");
        System.out.println(Thread.currentThread().getName() + ": Calling t.start()");
        t.start();
        System.out.println(Thread.currentThread().getName() + ": Calling t.join() to wait for thread exit");
        try {
            t.join();
        } catch (InterruptedException ie) {
            System.out.println(Thread.currentThread().getName() + ": Calling got InterruptedException");
        }
        System.out.println(Thread.currentThread().getName() + ": Program complete");
    }
}

示例输出:

main: Running main
main: Calling t.start()
main: Calling t.join() to wait for thread exit
Thread-0: Running run
main: Program complete

请注意我们的主线程刚刚调用start;然后JVM进行设置,以便在我们创建的线程上调用run

答案 1 :(得分:3)

您的困惑可能来自于使用术语线程创建初始化。没有指明它们究竟解决了什么容易误解,因为有多个概念,术语 thread 用于描述。

一方面,有Thread对象(通过新的Thread(...)构造函数创建)。就实际执行任何事情而言,不是线程。它只是一个 facade ,用于实现标准化API与OS / VM交互的线程机制。

创建一个Thread实例基本上与创建任何其他java对象没有任何不同。没有任何事情与实际向OS线程调度程序添加线程有关。

OS端的线程实际上是在(私有)本机方法start0()中创建的,start()方法执行一些健全性检查,然后调用start0()来实际创建OS级别的线程。

所以行动的一般顺序是:

  • 创建并初始化一个Thread-Object
    • 没有任何异常发生
  • 在上面的Thread-Object上调用start()
    • 委托本机代码创建操作系统级线程。 OS级别线程的入口点是java端的Thread.run()。
    • 在新线程上执行Thread.run()(运行委托的默认实现,使用Thread对象创建的Runnable,如果没有Runnable则不执行任何操作)

答案 2 :(得分:1)

这是start()和run()之间的主要区别:

  

Thread#start:当程序调用start()方法时,Java虚拟机会调用此线程的run方法。

     

Thread#run:如果直接调用run()方法,run()内的代码将在当前Thread上执行。

答案 3 :(得分:1)

  

如果我理解正确,说start()初始化一个新线程然后,如果你想在同一个线程中重新执行你的代码,你调用run()

这是不正确的。您需要了解每个Java程序都是使用 main 线程开始执行的。换句话说,当您运行具有main方法的Java程序(例如java MyProgram)时,将使用此堆栈底部的main方法创建新的执行堆栈。

如果程序在Thread方法中创建main实例并在线程实例上调用start方法,则将使用run创建新的执行堆栈堆栈底部的方法。您现在将拥有两个执行堆栈。一个在堆栈底部使用main方法,另一个在堆栈底部使用run方法。这两个堆栈可以并行完成它们的执行。

另一方面,如果在run方法中的线程实例上调用main,则只需在与main方法相同的执行堆栈中调用它。不会创建新的执行堆栈。因此,在线程实例上调用run与在任何其他对象上调用任何其他方法一样好,并且没有特殊含义。