让我们拥有以下代码(我们将在单核CPU上运行):
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("runnable_1_1");
System.out.println("runnable_1_2");
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
System.out.println("runnable_2_1");
System.out.println("runnable_2_2");
}
};
ExecutorService executorService = Executors.newSingleThreadExecutor(); // or Executors.newCachedThreadExecutor();
executorService.execute(runnable1);
executorService.execute(runnable2);
executorService.shutdown();
理论上理论上可能会将一个任务楔入另一个任务,我们会看到这样的输出:
runnable_1_1
runnable_2_1
runnable_2_2
runnable_1_2
此示例不强制使用单线程执行程序。强制要求我们只有一个CPU核心
答案 0 :(得分:4)
对于任意线程,这在Java级别上无法完成,因为没有非deprecated methods暂停它。所有有效的控制方法都包括主动屈服的线程。
但是,操作系统本身通常有一个调度程序,可以定期挂起并恢复所有正在运行的进程,从而可以获得比可用CPU核心数更多的进程。此外,Java虚拟机通常不会像单个进程一样运行(green threads属于过去),每个线程有一个进程。
因此,操作系统可能会暂停一个线程一小段时间,允许运行另一个线程或Java虚拟机外的其他一些进程。一般的答案可能是肯定的。
答案 1 :(得分:2)
当您尝试在此级别上推理代码时,CPU的数量无关紧要。理论上,您可以在操作系统上运行JVM,在每个程序指令之后强制执行上下文切换。它会很疯狂,没有操作系统可以做到这一点,但你怎么知道只看看Java代码?
如果它是单个线程执行器,答案是没有重叠,如果它不是单个线程执行器,则无法证明不会重叠。
要找到原因,我们需要查看JLS的第17章:
可以通过先发生关系来排序两个动作。如果一个 行动发生在另一个之前,然后第一个是可见的和 在第二天之前订购。
如果我们有两个动作x和y,我们写hb(x,y)来表示x 在y之前发生。
如果x和y是同一个线程的动作,x在y之前 程序顺序,然后是hb(x,y)。
在构造函数的结尾处有一个发生前的边缘 反对该对象的终结器(第12.6节)的开始。
如果动作x与后续动作y同步,那么我们也是 有hb(x,y)。
如果是hb(x,y)和hb(y,z),那么hb(x,z)。
类Object(第17.2.1节)的等待方法具有锁定和解锁功能 与他们有关的行动;他们发生在以前的关系之前 由这些相关行动定义。
应该注意到之前发生的关系 两次行动之间并不一定意味着必须采取行动 在实现中按顺序放置。如果重新排序产生 结果与法律执行一致,这不违法。
在单线程执行程序的情况下,这正是我们得到的:两个runnables是同一个线程的动作,一个将按程序顺序排在另一个之前。虽然最后一段允许重新排序,但重新排序不会导致正确同步代码中的明显差异。
有多个线程,这是任何人的猜测。只有两件事是有保障的:
PrintStream.println()
已同步。这就是理论。
实际上,对于大多数JVM实现和操作系统,您可能永远不会看到与此确切代码的重叠,原因很简单,因为您执行的任务将花费太少的时间来中断。让它们运行几分钟或几小时,你肯定会看到两者之间的上下文切换。
答案 2 :(得分:0)
[[@ biziclop的答案是正确的,虽然长而且令人困惑。 ]
从理论上讲,一个任务是否可以与另一个任务相结合,我们会看到这样的输出:
不在您发布的代码中。您正在向单个线程执行程序提交2个作业:
ExecutorService executorService = Executors.newSingleThreadExecutor();
这意味着只有1个线程将执行您的2 Runnable
。当第一个线程阻塞另一个Runnable
时没有执行,因此输出不会交错。第一个Runnable
需要在执行第二个Runnable
之前完成。
如果您使用Executors.newCachedThreadExecutor();
,则可以同时运行2 Runnable
并且它们的输出可以交错。在这种情况下,第一个Runnable
可能会打印出runnable_1_1
,然后进行时间切片,以便另一个线程可以执行并显示它的runnable_2_1
等。这是一个竞争条件然而,可能不太可能,但这是可能的。
此示例不强制使用单线程执行程序。强制要求我们只有一个CPU核心
正如@biziclop所提到的,硬件上的CPU数量并不重要。重要的是在任何一个时间点运行队列中有多少个线程。