我目前正在我的编程课上学习 Java 中的多线程。据我了解,无法预测不同线程的执行顺序,因此确保我们实施该机制以确保一切按所需顺序执行非常重要。
我学到的其中一种机制是使用 Thread.join()
强制一个线程等待其他线程完成执行。但是,下面的示例代码实际上让我更加困惑:
Runnable hello = () -> {
System.out.print("H");
System.out.print("e");
System.out.print("l");
System.out.print("l");
System.out.print("o\n");
};
Runnable world = () -> {
System.out.print("W");
System.out.print("o");
System.out.print("r");
System.out.print("l");
System.out.print("d\n");
};
Thread t1 = new Thread(hello);
Thread t2 = new Thread(world);
t1.start();
t2.start();
try {
t1.join();
t2.join();
System.out.println("Finished");
} catch (InterruptedException ie) {
System.out.println("Interruption");
}
据说这段代码应该演示 .join()
是如何工作的,它将执行 hello()
然后 world()
导致正确打印的 "Hello World"
。
但是,当我实际运行代码时,执行顺序似乎仍然是随机的。我得到了像
这样的结果// #1
WoHerld
llo
Finished
// #2
HWello
orld
Finished
// And others
那么,使用.join()
的目的是什么,执行顺序还像这样“乱七八糟”?
在这里我应该怎么做才能确保每次运行代码时都能得到正确打印的结果?
答案 0 :(得分:4)
在这种情况下加入仅表明最后一行“已完成”始终是正确的。两个线程单独打印字符无法保证顺序,因为它们都写入同一个输出缓冲区,在本例中为 stdout
(或 System.out
)。
保证执行顺序在使用锁或互斥体的范围内,在 Java 的情况下是 synchronized
关键字。
答案 1 :(得分:2)
join()
的目的是等待给定线程完成。它不会导致线程完成,或以任何其他方式改变线程的行为1。
如果您真的希望所有 t1
的输出都出现在所有 t2
的输出之前,那么您可以编写如下代码:
try {
t1.start();
t1.join();
t2.start();
t2.join();
System.out.println("Finished");
} catch (InterruptedException ie) {
System.out.println("Interruption");
}
然而,使用线程的目的是允许任务同时运行。如果您的问题要求您限制任务一个接一个地运行(即不能同时运行),那么线程无济于事。
因此,在您的示例中,获取顺序输出的简单方法是在同一线程上一个接一个地运行任务。并且(所有其他条件相同)您也可以在主线程上进行。
// Don't use threads at all
hello();
world();
1 - 事实上,如果 t.join()
调用确实以某种方式改变了 t
在调用之前的行为方式,那将是 causality violation。这是不可能用经典物理学或传统计算机实现的。当前的量子计算数学模型也不违反因果关系,但这在类 Java 线程的上下文中可能没有实际意义。
答案 2 :(得分:1)
如果这里的间隔是几秒钟呢? (你甚至可以通过在那里扔一个 Thread.sleep()
来模拟它)
t1.start();
t2.start();
// Here
t1.join();
t2.join();
当您调用 join
时,您的两个线程都已完成,因此这应该表明 join
对它们没有任何影响。
如果你想打印'Hello'然后'world',你可以重新排列顺序
t1.start();
t1.join();
t2.start();
t2.join();
当然,这里没有并发或并行。你只是在两个不同的线程上做一件事,然后又做另一件事。