与同步块in循环相关的Java并发性



我有一个简单的代码,将相同的对象传递给将用于锁定的不同类(不理想,但假设这是一种可能性)。输出应该是什么?

  1. MorningThread只打印&;Good morning &;在for循环内的synchronized块中。
  2. EveningThread只打印&;Good evening &;在for循环内的synchronized块中。
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。这是真的吗?(我找不到正确的Java文档调用这个)

根据我的理解,它可以是[输出]的各种组合(实际上)…

你这句话的这一部分是对的。

…但从理论上讲,它应该低于,因为"早晨"将首先执行,到这个时候"晚上"将等待,"晚上"将在"早晨"等待的时间执行,从今往后。

这个陈述是不正确的。不应该期望线程排序是理论上的或其他的。启动一个线程是很昂贵的(相对而言),所以在'good morning'线程启动之后,它会在'good evening'线程启动之前运行,锁,打印,锁,打印和结束。

Java线程被设计成异步运行,在大多数情况下,它们的操作顺序很难或不可能预测。以这种方式跑步是它们获得速度的方式。如果你将循环计数器增加到20000,那么你应该会看到一些混合输出,但它很可能是早上的块,然后是晚上的块,而不是ME ME ME…如果希望从线程程序中得到一个特定的有序输出,那么可能不应该使用线程,或者应该收集输出,然后在最后对其进行排序。

其他注释:

  • 锁对象应该是private final Object lock ...
  • 将锁对象注入到线程对象的构造函数中是一个很好的模式,而不是"不理想"。
  • System.out.println(...)也同步。System.out的任何使用都会改变线程程序的运行方式,因此应该谨慎使用。

JVM,甚至它的即时(JIT)编译器,都不能以有效的方式对不同线程中的流进行优化。

Java线程通常与本地操作系统(OS)线程密切相关。操作系统启动线程。这意味着一个线程可以在另一个线程启动之前完成!操作系统也暂停和恢复线程。这几乎可以在任何CPU指令上先发制人地发生。由于JVM或jit编译的代码都只是程序,在CPU指令中编码,它们通常甚至不会意识到它们被暂停或恢复。 因此,JIT编译器无法构建关于不同线程中的流的有意义的统计信息,更不用说优化它们了。