【问题标题】:JVM thread management v.s. OS schedulingJVM 线程管理与操作系统调度
【发布时间】:2015-03-30 20:59:30
【问题描述】:

据我所知,最常见的 JVM 并发 API 之一:futures - 至少在 scala 中实现 - 依靠用户代码在线程可能处于空闲状态时放弃它。在 scala 中,它通常被称为“避免阻塞”,开发人员必须在任何有意义的地方实现它。 效率不高。

是否存在 JVM 完全固有的东西,阻止 JVM 将线程的上下文切换到新任务 - 当线程空闲时 - 由操作系统进程调度程序实现?

【问题讨论】:

  • 更简洁地说,“JVM 是否使用用户模式线程?”? (当某事被阻塞时切换任务正是线程所做的
  • 虽然有些重叠,但这些不是同一个问题 :-)

标签: java multithreading scala jvm quasar


【解决方案1】:

是否存在 JVM 完全固有的东西,阻止 JVM 将线程的上下文切换到新任务 - 当线程空闲时 - 由操作系统进程调度程序实现?

大多数情况下,这种切换必须合作完成。每个单独的阻塞方法都必须以允许任务在完成后恢复的方式进行包装或重新实现,毕竟,不再有本地线程等待阻塞操作的完成。

虽然这对于 JVM 内部阻塞方法原则上可以完成,但考虑通过 JNI 执行的任意本机代码,JVM 不知道如何堆栈切换这些本机线程,它们毕竟卡在本机代码中。

您可能想看看quasar,据我了解,他们为一些JDK内部方法实现了这样的包装器或等效物,例如sleeppark/unpark、基于通道的- IO 和其他一些允许他们的纤程(以及因此在这些纤程上运行的期货)在等待完成时执行这种用户模式上下文切换的方法。

编辑:仅 JNI 已经足以限制用户模式任务切换为机会主义优化,当本机代码阻塞线程时,可能不得不回退到启动额外的本机线程。 p>

但这不是唯一的问题,例如在 linux 上,真正的异步文件 IO 操作需要文件系统和内核支持(参见SO question on AIO),并不是所有的都提供。如果没有提供它,则必须使用额外的阻塞 IO 线程来模拟它,从而重新引入我们一开始就想避免的所有开销。最好只是阻塞线程池本身并启动额外的线程,至少我们会避免这种方式的线程间通信。

内存映射文件还可以阻塞线程并强制操作系统调度程序由于页面错误而暂停线程,我不知道如何与虚拟内存系统合作来避免这种情况。

更不用说虚拟机上的所有阻塞调用必须使用操作系统提供的异步等效项重新实现。错过一个,你就会有一个阻塞的线程。如果您有一个阻塞的线程,您的线程池将需要一个自动增长功能,我们又回到了原点。

最后但并非最不重要的一点是,在某些情况下,可能需要阻塞、每个文件描述符一个线程 IO。保证用户模式切换所需的普遍更改可能会破坏这些。

总而言之,用户模式切换是可能的,有时。但是 JVM 无法对此做出硬性保证,因此无论如何它都必须实现所有本机线程处理,并且程序员将至少在某种程度上与执行这些未来的线程池的假设合作编写代码。有些情况可以消除,但不是全部。

【讨论】:

  • 谢谢!奇怪的是,我在一年前偶然发现了 Quasar,当时它似乎是“测试版”的东西,没有生产实现。但总的来说,有问题的方面是否在 JNI 等相当深奥的东西之外泛化?您能否进一步澄清/概括一下 JVM 架构中首先阻止抢占式调度的原因是什么?
  • 谢谢,多任务处理在 JVM 中不能像在操作系统中那样工作,这是多么可悲 ;(
  • 问题只是JVM(当前)使用内核线程来实现Java线程,并且这些线程具有相当高的开销。然而,我应该指出,偶尔阻塞内核线程是完全可以的,所以像虚拟内存分页这样的东西根本不会伤害你。作为 Quasar 的主要作者,我可以告诉您,Quasar 肯定已经准备好生产(并已用于生产)。