有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程为什么Kotlin/Java没有抢占式调度器选项?

CPU密集型任务可能会阻塞线程并延迟等待执行的其他任务。这是因为JVM不能中断正在运行的线程,需要程序员的帮助和手动中断

因此,在Java/Kotlin中编写CPU绑定的任务需要手动干预才能使事情顺利运行,就像在下面的代码中使用Kotlin中的Sequence

fun simple(): Sequence<Int> = sequence { // sequence builder
    for (i in 1..3) {
        Thread.sleep(100) // pretend we are computing it
        yield(i) // yield next value
    }
}

fun main() {
    simple().forEach { value -> println(value) } 
}

据我所知,原因是具有中断运行线程能力的抢占式调度程序会带来性能开销

但是有一个开关不是更好吗,这样你就可以选择了?如果您希望使用更快的非抢占式调度程序运行JVM。或者使用速度较慢的预emtpive(在N个指令后中断和切换踏板),但能够平稳运行,并且不需要手动操作

我想知道为什么Java/Kotlin没有这样的JVM开关,允许选择您想要的模式


共 (2) 个答案

  1. # 1 楼答案

    这个问题基于一个错误的前提:在JVM中,抢占式调度程序是您唯一的选择没有现代JVM使用协同多任务

    现代的JVM都没有实现用户空间线程或自己的调度器。JVM使用本机操作系统线程。本机线程由操作系统调度,而操作系统调度程序抢占式的

    JVM线程1对1映射到本机操作系统线程这一事实对于需要高并发性的应用程序来说是个问题。线程相对稀少且昂贵。为了解决这个问题,Project Loom正在研究添加“虚拟线程”,这样可以更节省地使用本机线程,尤其是对于I/O绑定的任务

    ProjectLowe正在积极开发中,它何时成为标准Java的一部分还没有固定的时间表。关于Project Lowe如何调度“虚拟线程”,Project Lowe中的latest (May 2020) update声称“虚拟线程是抢占式的,而不是协作式的”,但接着又说,“JDK中目前没有一个调度器使用基于时间片的虚拟线程抢占”。听起来ProjectLoom中的“虚拟线程”调度器在其当前状态下介于完全合作和完全先发制人之间。看看这个项目是如何发展的,以及当它集成到主流Java中时我们将得到什么,这将是一件有趣的事情

    July 28 Q&A the Loom project lead Ron Pressler中提到,您可以为虚拟线程插入自己的调度程序,但没有详细说明您对调度算法的控制程度

  2. # 2 楼答案

    当您使用Kotlin协程或Java虚拟线程(在Loom之后)编程时,您可以从操作系统获得抢占式调度

    按照通常的做法,未被阻塞的任务(即它们需要CPU)在Kotlin default dispatcher或Java ForkJoinPool中的真实OS线程上进行多路复用。这些操作系统线程由操作系统预先调度

    然而,与旧式多线程不同的是,当任务被阻塞等待I/O时,它们不会被分配给线程。这在抢占方面没有什么区别,因为等待I/O的任务无论如何都不可能抢占另一个正在运行的任务

    当使用协同程序进行编程时,你不会同时获得大量任务的抢占式调度。如果有许多任务需要CPU,那么前N个任务将被分配给一个真正的线程,操作系统将对它们进行时间切片。剩下的人将在队列中等待,直到这些人完成

    但在现实生活中,当你有10000个任务需要同时进行交互时,它们就是I/O绑定的任务。平均而言,一次需要CPU的线程并不多,因此从默认的dispatcher或ForkJoinPool获得的实际线程数量非常多在正常操作中,等待线程的任务队列几乎总是空的

    如果你真的遇到了这样一种情况:10000个CPU限制的任务需要同时进行交互,那么你无论如何都会感到悲伤,因为时间分割并不能提供一种非常流畅的体验