简体   繁体   English

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

[英]Need some clarification on Java threads: start() and run()

According to the StackOverflow answers, start() creates a thread and executes the code in this new thread and run() executes the code in the current thread without creating any - as if it was a Runnable 根据StackOverflow的答案, start()创建一个线程并在此新线程中执行代码, run()在当前线程中执行该代码而不创建任何代码-好像它是Runnable

But the docs, if I understood them properly, say that start() initializes a new thread an then, if you want to re-execute your code in the same thread, you call run() 但是文档,如果我能正确理解它们,则说start()初始化了一个新线程,然后,如果要在同一线程中重新执行代码,则调用run()

Which one is right? 哪一个是对的?

UPD UPD

Okay, looks like calling run() manually is not a good practice and is at least useless. 好的,看起来手动调用run()并不是一种好习惯,并且至少没有用。 Are there some good ways to call the code in run() ? 有一些好的方法可以在run()调用代码吗? Say, I have a relatively slow action that is required frequently, one instance at a time - what then? 说,我有一个相对缓慢的动作,经常需要一次,一次实例-那么该怎么办?

As quoted, neither of them is right. 引,它们都不是正确的。

start doesn't create or initialize a thread. start不会创建或初始化线程。 It starts a thread. 启动一个线程。 From the documentation : 文档中

Causes this thread to begin execution; 使该线程开始执行; the Java Virtual Machine calls the run method of this thread. Java虚拟机将调用此线程的run方法。

The thread is created and initialized by calling the Thread constructor, which naturally you have to do prior to calling start . 线程是通过调用Thread构造函数创建和初始化的,这自然是您必须在调用start之前完成的。 (That's the "external myth," anyway; I'd have to check the JVM spec, but I suspect it would probably be valid for a JVM to defer actual creation of the OS thread until/unless you called start .) (这就是“外部的神话,”无论如何,我得检查JVM规范,但我怀疑这很可能是有效的JVM推迟实际创建操作系统的线程,直到/除非你叫的start 。)

In the normal case, you never call the run method of a Thread instance directly. 在正常情况下,您永远不会直接调用Thread实例的run方法。 Instead, you start the thread via start and the JVM schedules a call to run on the actual thread assigned to that Thread instance. 取而代之的是,您通过start启动线程,并且JVM安排了一个调用,以在分配给该Thread实例的实际线程上run If you call run directly, it gets called on the thread you're using to do that, which probably isn't the one assigned to the Thread instance. 如果直接调用run ,它将在您正在使用的线程上被调用,这可能不是分配给Thread实例的Thread

You may find the Java concurrency tutorial useful. 您可能会发现Java并发教程很有用。


Re your comment below: 在下面重新发表您的评论:

And how to re-execute the code in run() ? 以及如何重新执行run()的代码? Restart the thread or have some infinite loop with a boolean value controlling the execution? 重新启动线程还是通过boolean值控制执行有一些无限循环?

The latter, but not an infinite loop, a loop with a termination condition. 后者(但不是无限循环)是具有终止条件的循环。 From the start documentation: start文档:

It is never legal to start a thread more than once. 一次启动一个线程永远是不合法的。 In particular, a thread may not be restarted once it has completed execution. 特别是,线程一旦完成执行就可能不会重新启动。

So once run returns, that's it, you can't keep using the thread. 因此,一旦run返回,就可以继续使用线程。 Instead, you have the thread not return from run until its work is done. 取而代之的是,线程必须等到其工作完成后才能从run返回。 If you want to keep it around waiting for work, it should wait on a semaphore of some kind that you can signal from outside the thread, to wake it up to do some work. 如果您想让它一直等待工作,它应该等待某种信号量,您可以从线程外部发出信号,以唤醒它来做一些工作。

A subsection of the tutorial above talks about one way to do that with Guarded Blocks using wait and notify / notifyAll . 上面的教程的一个小节讨论了使用waitnotify / notifyAll来使用Guarded Blocks的一种方法。


Here's a simple example where every call to System.out.println prints the name of the thread on which the call was made so you can see which thread does what: 这是一个简单的示例,其中对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");
    }
}

Example output: 输出示例:

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

Note that our main thread just called start ; 请注意,我们的主线程仅称为start then the JVM set things up so that run would be called on the thread we'd created. 然后JVM进行设置,以便在我们创建的线程上调用run

Your confusion probably comes from the ambigous use of the terms thread , create and initialize . 您的困惑可能是由于术语“ 线程” ,“ 创建”和“ 初始化”的使用过多而引起的。 Without specifying what they exactly address its easy to misunderstand, because there are multiple concepts that the term thread is used to describe. 由于没有使用术语“ 线程”来描述多个概念,因此没有明确说明它们的确切含义很容易造成误解。

On one hand, there is the Thread object (that is created through new Thread(...) constructor). 一方面,存在Thread对象(通过新的Thread(...)构造函数创建)。 Thats not the thread in terms of actually executing anything. 不是实际执行任何事情的线程。 Its just a facade for the threading mechanism that implements a standardized API to interact with the OS/VM. 它只是实现了一个标准的API与OS / VM交互的线程机制一个门面

Just creating a Thread instance is basically not any different from creating any other java object. 基本上,仅创建Thread实例与创建任何其他Java对象没有什么不同 Nothing happens yet that has anything to do with actually adding a thread to the OS thread scheduler. 尚未发生任何与将线程实际添加到OS线程调度程序有关的事情。

The thread on the OS side is actually created in a (private) native method start0(), the start() method performs some sanity checking, then calls start0() to actually create an OS-Level thread. 操作系统端的线程实际上是在(私有)本机方法start0()中创建的,start()方法执行了一些健全性检查,然后调用start0()来实际创建OS级线程。

So the general order of actions is: 因此,一般的操作顺序为:

  • Create and Initialize a Thread-Object 创建和初始化线程对象
    • Nothing out of the ordinary happens yet 一切都没有发生
  • You invoke start() on above Thread-Object 您在上述Thread-Object上调用start()
    • Delegates to native code to create an OS-Level thread. 委托本机代码创建 OS级线程。 Entry point for the OS-Level thread is Thread.run() on the java side. OS级线程的入口点是Java端的Thread.run()。
    • Executes Thread.run() on the new thread (default implementation of run delegates to the Runnable the Thread object was created with, or does nothing if no Runnable) 在新线程上执行Thread.run()(运行委托的默认实现是创建Thread对象的Runnable,如果没有Runnable,则不执行任何操作)

Here is the main difference between start() and run(): 这是start()和run()之间的主要区别:

Thread#start : when program calls start() method the Java Virtual Machine calls the run method of this thread. Thread#start :当程序调用start()方法时,Java虚拟机将调用此线程的run方法。

Thread#run : If you call run() method directly code inside run() will execute on current Thread. Thread#run :如果直接调用run()方法,则run()内部的代码将在当前Thread上执行。

if I understood them properly, say that start() initializes a new thread an then, if you want to re-execute your code in the same thread, you call run() 如果我正确理解它们,则说start()初始化一个新线程,然后,如果要在同一线程中重新执行代码,则调用run()

This is incorrect. 这是不正确的。 You need to understand that every Java program begins it's execution with the main thread. 您需要了解,每个Java程序都是从线程开始执行的。 In other words, when you run a Java program that has a main method (eg java MyProgram), a new execution stack is created with the main method at the bottom of this stack. 换句话说,当您运行具有main方法的Java程序(例如java MyProgram)时,将创建一个新的执行堆栈,并且main方法位于该堆栈的底部。

If a program creates a Thread instance in the main method and calls the start method on the thread instance, a new execution stack will be created with the run method at the bottom of the stack. 如果程序在main方法中创建一个Thread实例并在该线程实例上调用start方法,则将创建一个新的执行堆栈,并在堆栈底部使用run方法。 You will now have two execution stacks. 您现在将具有两个执行堆栈。 One with the main method at the bottom of the stack and the other with the run method at the bottom of the stack. 一种是在堆栈底部使用main方法,另一种是在堆栈底部使用run方法。 These two stacks can complete their execution in parallel. 这两个堆栈可以并行完成其执行。

On the other hand, If you call run on a thread instance in the main method instead, it will simply be called in the same execution stack as the main method. 另一方面,如果改为在main方法中调用线程实例上的run ,则它将简单地在与main方法相同的执行堆栈中调用。 A new execution stack will not be created. 将不会创建新的执行堆栈。 Therefore, calling run on a thread instance is as good as calling any other method on any other object and has no special meaning. 因此,在线程实例上调用run与在任何其他对象上调用任何其他方法一样好,并且没有特殊含义。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM