【发布时间】:2021-08-29 23:43:14
【问题描述】:
我有一个简单的代码,它将同一个对象传递给另一个用于锁定的类(不理想,但假设这是一种可能性)。输出应该是什么?
- MorningThread 只是在 for 循环内的同步块中打印“早安”。
- EveningThread 只是在 for 循环内的同步块中打印“晚安”。
private Object lock;
MorningThread(Object lock) {
this.lock= lock;
}
public void run() {
for (int i = 0; i < 2; i++) {
synchronized(lock) {
System.out.println("Good Morning");
}
}
}
}
class EveningThread extends Thread {
private Object lock;
EveningThread(Object lock) {
this.lock= lock;
}
public void run() {
for (int i = 0; i < 2; i++) {
synchronized(lock) {
System.out.println("Good Evening");
}
}
}
}
public class HelloWorld {
public static void main(String[] args) throws Exception {
Object lock = new Object();
new MorningThread(lock ).start();
new EveningThread(lock ).start();
}
}
根据我的理解,它可以是各种组合(实际上),但从理论上讲它应该在下面,因为早上会先执行,到这个时候晚上会等待,晚上会在早上等待的时候执行,以后再执行。
- 早安
- 晚上好
- 早安
- 晚上好
但奇怪的是它总是(当我在 Windows 机器上运行时)
- 早安
- 早安
- 晚上好
- 晚上好
所以我开始认为 JVM 可能会进行一些优化,它可以识别持续流入并且不会锁定 Evening 直到 Morning 结束。那可能是真的吗? (我找不到正确的 Java 文档来调用它)
【问题讨论】:
-
两个输出都是完全有效和合理的。无法准确预测线程将如何安排执行,因为许多系统条件会影响它们。 Windows 是否正在下载更新?扫描驱动器?写出磁盘缓存?所有这些都占用一个 CPU 内核,因此会影响线程计划运行的方式和时间。您的 MorningThread 完全有可能在 EveningThread 启动之前执行并完成。
-
内循环是如此之快,以至于无论哪个线程先启动,都可能在第二个线程甚至试图获得锁之前完成。
-
可能无法观察到某些排列。 JIT 可以展开一个循环,然后进行锁粗化。
标签: java multithreading concurrency thread-safety