美文网首页
OpenJDK为什么采用JVM线程和内核线程1:1的模型?

OpenJDK为什么采用JVM线程和内核线程1:1的模型?

作者: flycash | 来源:发表于2019-02-03 20:28 被阅读27次

众所周知的是,在Linux平台上,OpenJDK采用的是One-to-One线程模型。也就是,每一个JVM线程,都有一个对应的内核线程。这篇文章就是试图讨论一下,为什么会做出这样一种设计。

在上古年代,或者说在众多JVM的实现中,并不是所有的JVM实现都是采用这种模型的。比如说在早期的Solaris上,JVM的实现采用的是Many-to-One模型。也就是说,所有的JVM线程对应到了一个内核线程上。

除了这里提到了One-to-One线程模型和Many-to-One线程模型,还有一种Many-to-Many模型。这种模型是将M个用户线程映射到N个内核线程上。一般而言,M是大于N的。后面Solaris的线程模型就切换到了Many-to-Many模型上。
用Solaris的例子来感受一下。


Many-to-One One-to-One Many-to-Many

所以,“OpenJDK为什么使用One-to-One模型”的答案实际上就是这三种模型优缺点的对比。

Many-to-One

使用Many-to-One模型,在早些年看来,是一个比较不错的决定。首要的一个理由是,这种模型比较“成熟”,除了JVM,还要很多东西也是采用这种模型。而且在那个CPU核数不多的年代,"Many-to-One"模型在单核平台上,与其余模型比起来,反而有一些性能优势。

但是这种模型在多核CPU上会带来严重的性能问题,即该模型只能运行在其中的一个核上,完全无法利用多核CPU。

另外一个问题是,用户级线程相互之间会干扰。一种显而易见的情况是,某个Java线程执行了某个系统调用,而导致内核线程阻塞的时候,其余的线程,也会全部被阻塞掉。

这个东西就有点像TCP多路复用带来的问题。Stream1丢失了某个包,而将Stream2, Stream3全部阻塞了。

Many-to-Many

随着硬件的发展和内核的更新换代。Many-to-Many模型和One-to-One模型变得更加常见。

Many-to-Many模型有效的解决了无法利用多核CPU的问题。也就是说,给JVM带来了真正的并行。JVM线程被绑定到不同的内核线程上,这些内核可以分别被不同的CPU核所调度。

但是线程之间相互影响的问题还是没有解决。也就是,某几个JVM线程被映射到一个内核线程后,如果这里面的一个JVM线程发起系统调用导致内核线程阻塞,那么剩下的几个线程依旧会被阻塞。

One-to-One

这是我们最为熟知的一种模型。它解决了无法利用多核CPU和线程相互之间影响两个问题。

在这种模型之下,JVM直接依赖于平台的线程库。包括线程调度、通信之类的,几乎都依赖于内核线程的机制。JVM线程更加多的看做是一个内核线程的包装,有点语法糖的意思。因为每一个JVM线程都绑定到了一个内核线程,所以即便一个线程阻塞,也不会影响别的线程

性能问题

最后来比较一下性能。当然我这个菜鸡是没有那个能耐来做实验的,我来引用一下别人做的实验的数据。这些数据是在Linux平台上,采用Green Thread和内核线程的JVM的实现的对比。这部分内容引用自《Comparative performance evaluation of Java threads
for embedded applications: Linux Thread vs. Green Thread》


上下文切换

这是使用yield()时候上下文切换的性能对比。可以看到的是,在线程数不多的时候,使用内核线程的性能甚至稍微要比使用用户级线程的差一点。而在线程多了以后,使用内核线程有10%的性能提高。

这个是有点违反直觉的。因为一般的想法是,用户级线程的切换,应该要比内核线程的上下文调度要快。我也不知道该怎么解释这个结果……

线程控制

这个结果就十分符合我们的预期。使用内核线程的时候,无论是线程创建还是调度,都依赖于系统调用。相比之下,使用用户级线程就没有这种烦恼。系统调用的低效和昂贵,也一览无余了。

线程同步

这部分性能也是这样,用户级线程拥有很大的优势。

IO阻塞

这个我觉得并没有太大的参考价值。因为用户级线程使用的是非阻塞IO,避开了线程阻塞这个天坑。不过即便如此,它还是要稍微比内核线程慢一点。

代码执行

用户级线程也是明显占优。

总结

我来总结一下。如果按照最后的性能分析来看,那么显然OpenJDK使用One-to-One的模型是有点傻气的。不过我对这个性能测试持有一种怀疑的态度。

但是,其实现在有一种趋势,就是协程。协程很大程度上就是为了避免线程调度的开销,试图在应用层面上解决这种问题。所以这种解决问题的思路,就比较贴近用户级线程。在Kotlin1.3中,已经正式支持协程了。等有空我来分析一下Kotlin协程的实现。

公众号

相关文章

  • OpenJDK为什么采用JVM线程和内核线程1:1的模型?

    众所周知的是,在Linux平台上,OpenJDK采用的是One-to-One线程模型。也就是,每一个JVM线程,都...

  • 2021-02-23-jvm

    1、请介绍⼀下 JVM 内存模型?⽤过什么垃圾回收器 Java内存模型结构分为线程共享区和线程私有区 线程共享区:...

  • 线程模型的3种实现

    线程的实现模型主要有3种:内核级线程模型、用户级线程模型和混合型线程模型。它们之间最大的区别在于线程与内核调度实体...

  • 操作系统线程问题

    内核线程和用户线程区别: 内核未知的是用户级线程, 而内核层的线程则知道内核线程。 在使用 M:1 或 M:1N映...

  • JVM面试常问基础总结

    JVM内存模型 JVM垃圾回收 1. JVM内存模型 线程隔离的三个区:程序计数器:当前线程所执行的行号指示器,指...

  • 三、操作系统之线程

    前言 什么是线程 引入线程的原因 线程的概念 线程和进程的关系 线程结构 线程有点 多线程模型 用户线程和内核线程...

  • JVM 内存模型

    1.内存模型 JVM 内存分为 线程私有区 和 线程共享区。 线程私有区:程序计数器用作 多线程切换虚拟机栈管理J...

  • Java与线程

    1. 线程的实现 三种方式 1. 使用内核线程实现 内核线程的定义:内核线程(Kernel-Level Threa...

  • 线程模型以及goroutine协程的实现方式

    一、线程模型 1.1 内核级线程模型 每个线程由内核调度器独立的调度,所以如果一个线程阻塞则不影响其他的线程。 优...

  • 多线程模型

    线程支持有两种,一种是用户线程,一种是内核线程 用户线程和内核线程之间存在某种关系,有多对一模型,一对一模型,多对...

网友评论

      本文标题:OpenJDK为什么采用JVM线程和内核线程1:1的模型?

      本文链接:https://www.haomeiwen.com/subject/xykusqtx.html