2

我的笔记说线程调度算法有两大类,抢占式和时间共享。我想弄清楚这些在 Java 中是如何工作的。

据我了解(如果我稍微错了,请纠正我!)抢占式允许较高优先级的线程在进入可运行状态时从较低优先级的线程接管 CPU。它会独占 CPU,直到更高优先级的线程出现,还是会简单地消耗大部分 CPU 时间,但低优先级的线程也会有机会运行?我在这里假设没有调用会放弃 CPU 的方法,如 yield() 或 sleep()。

在时间共享中,更高优先级的线程是否会获得更大的 CPU 时间份额?

我猜我对先发制人的解释(以及我提出的问题)是不正确的,因为它似乎与时间共享相同!但我想确定细节。

我想,这一切是如何实现的?JVM 或操作系统是否调度线程,还是依赖于 JVM 实现?

4

4 回答 4

5

首先,我怀疑您的笔记不正确,并且应该是非抢占式而不是分时(也称为抢占式)。至少,几十年前我参加操作系统课程时的崩溃是这样的,我怀疑它已经改变了。

抢占式线程 (/process) 模型中,每个线程 (/process) 都被授予对 CPU 的唯一使用权,直到它明确放弃它。这是您在诸如原始 Windows 或 Macintosh 等单用户操作系统(以及在它们之前的一堆小型计算机系统)中找到的模型。正在运行的进程可以显式地yield()控制其对处理器的控制,从而允许另一个线程/进程运行。当然,对此有很多警告,特别是关于中断处理,但我将把描述留在那里。

在抢占式,即分时模型中,操作系统可能会强制线程/进程让出 CPU(即“抢占”它)。这可能发生在任何数量的地方,但在最简单的情况下,操作系统会在时钟的每个滴答声中被调用,并决定哪个进程应该在时钟的下一个滴答声中获得 CPU。再次简化,具有最高优先级的进程开始运行;如果有多个进程具有相同的优先级,则通常由最近运行的算法决定。

但是,无论如何,对于所有现代 Java 实现,决定线程何时(以及在何处)运行的是操作系统,而不是 JVM。

于 2013-05-23T14:19:24.240 回答
1

不是线程专家,但让线程无限期运行会导致饥饿。您仍然会在一段时间内给予较低优先级的优先级。我相信时间共享使所有线程的数量基本相同。这些笔记可能会有所帮助。

从所说的笔记:

非抢占式调度:当前进程通过终止或切换到等待状态来释放 CPU。(用于 MS Windows 系列)

  • 优点:
    • 减少周转时间
    • 不需要特殊的硬件(例如,定时器)
  • 缺点
    • 调度算法的选择有限

抢占式调度:当更重要的进程插入就绪队列或分配的 CPU 时间已经过去时,当前进程需要不由自主地释放 CPU。(用于 Unix 和类 Unix 系统)

  • 优点:
    • 调度算法的选择没有限制
  • 缺点:
    • 额外的开销(例如,更频繁的上下文切换、硬件计时器、对数据的协调访问等)
于 2013-05-23T14:13:22.973 回答
0

这个答案可以在另一个来源上找到..我很困惑。但无论如何都要发布。我实际上并不了解 Java 规范或 JVM 的实际实现方式。但是,这是我对此的推理。有人提到 JVM 可以在操作系统上只运行一个线程,并在 JVM 内部进行自己的调度。对我来说,这没有意义,因为与使用多个真正的操作系统级线程的其他软件相比,您的 Java 软件的性能会差得多(在简化的视图中,每个操作系统线程将获得相同的时间,而几个 Java线程必须共享单个操作系统线程的时间)。

因此,JVM 更有可能为您在 Java 中创建的每个线程启动一个操作系统级线程。然后由操作系统来安排线程。根据 JVM 的实现和操作系统的能力,在 Java 中指定线程优先级可以不同地映射到 OS 线程。例如,对于 Linux,优先级与循环调度相结合。大多数情况下,它会随着为线程安排的时间量而变化。时间片不应该变成零长度,这样可以防止线程完全饥饿。但是,有可能在 Linux 中使用实时线程(可能无法从 Java 访问)以使操作系统完全无响应。

我猜Java规范提到了抢占式线程调度,因为只有两种主要的多线程方式:抢占式调度和协作多线程。对于抢占式多线程,您需要硬件和操作系统的支持。另一方面,协作多线程意味着您必须在源代码中调用一个函数才能在线程之间主动切换。如果您自己的源代码不调用此函数,则在协作多线程中没有其他线程可以运行。

作为对您还询问有关线程池的答案之一的回应。通常,线程池的使用方式如下:您有一个线程数固定的池。例如,这可能是每个内核一个线程。创建线程池后,所有线程都没有任何工作要做。现在,您可以将任务分配给池。然后,池将使用其线程最终为每个任务提供结果。这样做的好处是您可以将比实际可用线程更多的任务放入池中。这是一种简单的方法,可以避免一次用太多线程使系统过载(每个任务同时使用一个线程)。要了解有关此的更多信息,您可以寻找“期货”。

Java中的线程调度是如何完成的?来自:https ://www.researchgate.net/post/How_is_Thread_scheduling_in_Java_done [2017 年 5 月 22 日访问]。

于 2017-05-22T17:25:03.893 回答
0

实际上,线程是一个抽象概念。它只是一个应该执行的原子进程。原子,我的意思是一个进程,如果你停止它(例如,因为它太长并且你想执行另一个),你需要存储它的当前状态并在继续执行该进程之前恢复它。现在,过程中的内容取决于您想要的内容。例如,如果我没记错的话,在 C 语言中,一种旨在构建操作系统的语言,进程基本上就是一个程序。该程序可以要求系统运行其他程序,或者派生自己以运行克隆(通过使用 fork() 函数)。无论是不同的程序还是克隆,无论如何它都是不同的进程,应该以原子方式执行。在 C 语言中,线程就是程序。在 Java 中,线程是扩展 Thread 的类。在 C 中,你通过执行它的 main() 函数来启动一个线程/程序,在 Java 中你通过执行它的 run() 方法来运行一个线程/类。这仅适用于线程概念。它只是程序或执行的线程实例的通用名称。

如今,计算机需要并行执行许多事情。它导致同时管理多个线程。这是线程池:它是要执行的线程列表。而已。一个简单的清单。但是有了这个,你就有了调度程序:你有给定数量的“处理器”,你需要决定在它们上执行哪些线程,但是你不能执行比你拥有的“处理器”更多的线程。调度器策略应该设计为尽可能多地节省时间,这取决于线程的内容和依赖关系。可以使用不同的策略,例如平衡的执行时间、循环、优先级等。

我将“处理器”放在引号中,因为我的意思是“计算单元”。它可以是计算机的物理处理器,也可以是 CPU,现在通常有 2 到 8 个内核(意味着它可以同时运行 2-8 个线程),并执行 CPU 指令(通常是 RISC)。它可以是显卡的处理器,也可以是 GPU,它有几十个或几百个内核,但使用不同的指令集。它也可以是来自 Java 虚拟机或 JVM 的处理器,它只能运行单个线程并使用自己的一组指令(字节码)。JVM 与前两个的主要区别在于 CPU 和 GPU 直接与硬件交互,无需中介,而 JVM 将自己的字节码转换为 RISC 指令并请求 CPU 执行它们。

CPU 使用自己的调度程序来执行其线程(RISC 指令块,而不仅仅是一个)。让我们称之为“系统线程”。但是当您使用 Java 编程时,您的执行环境不是系统,而是 JVM,它为您提供了系统的抽象(这就是您可以在任何机器上运行 Java 程序而无需关心下面的系统的方式)。这意味着不允许您直接与系统通信,因此 Java 为您提供了一种在其自身级别处理线程的方法,允许您创建线程 (new Thread()) 并将它们提供给调度程序 (SwingUtilities,执行人等)。您的 Java 线程如何转换为系统线程,以及您的 Java 调度程序如何用于替换系统调度程序,是 JVM 的工作。

因此,当您谈论 Java 中的线程时,您不会谈论系统级别的线程。这是 JVM,它将与系统交互,以便您有类似的行为。但是您的 Java 线程不是由 CPU 管理的,因此您可以请求特定的调度策略,而与 CPU 的调度策略无关。

Java中的线程调度是如何完成的?来自:https ://www.researchgate.net/post/How_is_Thread_scheduling_in_Java_done [2017 年 5 月 22 日访问]。

于 2017-05-22T17:12:26.917 回答