Java 这个线程join()是如何工作的?

Java 这个线程join()是如何工作的?,java,multithreading,Java,Multithreading,我是java新手,目前正在使用一个培训材料,下面的代码生成以下输出: Run. Run. doIt 它是如何运行的。两次?t.join()是如何工作的 t.start()开始执行新线程,新线程将在t.run()中执行代码。但是对t.start()的调用将立即返回;它不会等待该线程完成执行run()。t.start()返回后,主线程运行t.run()。最后,它将等待t线程完成,这就是t.join()所做的。此方法不会立即返回,而是等待线程完成。(碰巧的是,在这种情况下速度会非常快——但可能需要几

我是java新手,目前正在使用一个培训材料,下面的代码生成以下输出:

Run. Run. doIt
它是如何运行的。两次?t.join()是如何工作的

t.start()
开始执行新线程,新线程将在
t.run()
中执行代码。但是对
t.start()
的调用将立即返回;它不会等待该线程完成执行
run()
t.start()
返回后,主线程运行
t.run()
。最后,它将等待
t
线程完成,这就是
t.join()
所做的。此方法不会立即返回,而是等待线程完成。(碰巧的是,在这种情况下速度会非常快——但可能需要几分钟、几个小时甚至永远。)

控制流如下所示:

Thread A
|
+- t.start() ---> starts Thread B
+- t.run()               +- t.run()
|                        |
+- t.join() waits for B  |
|           to finish    |
|               \------> +- Thread B stops
+- t.doIt()
+- Thread A stops
这是一个无关的电话

当您使用
t.start()
启动线程时,它是独立启动的。这会将“运行”打印到屏幕上

然后,
t.run(
运行线程的
run(
方法,该方法也会打印
run
。“run”的这两个打印可以是任意顺序

最后,我们调用
t.join
。这将等待线程终止(在通过join设计打印线程之后,以及在打印from run之后,因为
run
调用在前和阻塞之前)

join
之后,doIt函数以阻塞方式调用

简而言之,主线程和
t
线程都打印
run
,然后主线程等待
t
线程完成打印
doIt

尽管许多调用是
t
线程的一部分,但线程的行为在某种程度上独立于实例。

方法
start()
会导致线程对象执行(当它执行时是不确定的),JVM会调用
run()
方法

但是您也可以直接调用
run()
,这就是为什么
run()
执行两次

调用
join()

RRuunn..  doIt

真讨厌。这是来自Safeway编程学院的吗?我推荐线程上的“编程洞穴”视频-为什么我的评论总是吸引“粗鲁或无礼”的旗帜?我并不是在侮辱任何人(也许除了编写该示例的讲师)。作为记录,我通常不赞成通过使用坏代码来教人们好的编程。@RobertHarvey也许这个例子是为了说明
start()
调用
run())
,或者说
线程
仍然是一个具有可调用方法的常规类,或者其他什么。我同意这是一个令人憎恶的问题。这并没有什么真正的答案。@iamnotmaynard我认为这是显而易见的,但我现在已经解释清楚了连接确保在执行之前打印两条运行消息。@inc哦,是的……太早了:/@Bohemian我是有点口语化。收紧了语言。需要注意的另一点是调用start()实际上并没有启动线程。它只是请求JVM启动它;启动时间是不确定的。@yshavit:可能是个愚蠢的问题,但这是我的理解:run()方法是线程的默认方法,但可以重写。因此,每当线程启动时,它都只自动执行run()。在这里,在两个run()完成后,doIt()会被显式调用,因此它也会运行。这是对的吗?如果是,为什么运行()呢在B中自动执行,但从OS/JVM实现的角度来看,在A中显式调用?@Bohemian,您可能是对的。从Java的角度来看,这是一个迂腐、无意义和错误之间的区别。文档中说它“导致此线程开始执行”。JLS§17说“当start()启动时,线程将启动”方法在相应的线程对象上被调用。“当然,当
run()
中的第一个操作被调度时是另一个问题。@mankand007你是对的。但是要认识到的是,即使
run()
是“特殊的”因为它是一个新线程将执行的方法,它也是一个普通的、普通的方法,您可以在任何地方和任何您喜欢的地方显式调用。想象一下,如果我有一个方法
public void goFoo(){foo();}
。调用
goFoo()
将导致
foo()
运行,但是
foo()
也是一个可以显式调用的普通方法。同样的想法。这是否意味着即使没有t.run()也可以,结果将是相同的?@mankand007 No.
运行
输出中的一个将被忽略,因为打印从未被调用两次。请尝试并查看。
PrintStream.print
委托给一个私有的,它是同步的。因此,除非您将
System.out
替换为一个不同步的自定义子类
PrintStream
ronize,你不会在实践中看到这一点。不过,
PrintStream
合同并不能保证这一点;它只是一个方便的“不需要,但在实践中有效”的东西。
t.run();
RRuunn..  doIt