死磕 java线程系列之线程模型

问题

(1)线程类型有哪些?java

(2)线程模型有哪些?python

(3)各语言使用的是哪一种线程模型?编程

简介

在Java中,咱们平时所说的并发编程、多线程、共享资源等概念都是与线程相关的,这里所说的线程实际上应该叫做“用户线程”,而对应到操做系统,还有另一种线程叫做“内核线程”。segmentfault

用户线程位于内核之上,它的管理无需内核支持;而内核线程由操做系统来直接支持与管理。几乎全部的现代操做系统,包括 Windows、Linux、Mac OS X 和 Solaris,都支持内核线程。多线程

最终,用户线程和内核线程之间必然存在某种关系,本章咱们一块儿来学习下创建这种关系常见的三种方法:多对一模型、一对一模型和多对多模型。并发

多对一模型

thread model

多对一线程模型,又叫做用户级线程模型,即多个用户线程对应到同一个内核线程上,线程的建立、调度、同步的全部细节所有由进程的用户空间线程库来处理。高并发

优势:性能

  • 用户线程的不少操做对内核来讲都是透明的,不须要用户态和内核态的频繁切换,使线程的建立、调度、同步等很是快;

缺点:学习

  • 因为多个用户线程对应到同一个内核线程,若是其中一个用户线程阻塞,那么该其余用户线程也没法执行;
  • 内核并不知道用户态有哪些线程,没法像内核线程同样实现较完整的调度、优先级等;

许多语言实现的协程库基本上都属于这种方式,好比python的gevent。spa

一对一模型

thread model

一对一模型,又叫做内核级线程模型,即一个用户线程对应一个内核线程,内核负责每一个线程的调度,能够调度到其余处理器上面。

优势:

  • 实现简单【本篇文章由公众号“彤哥读源码”原创】;

缺点:

  • 对用户线程的大部分操做都会映射到内核线程上,引发用户态和内核态的频繁切换;
  • 内核为每一个线程都映射调度实体,若是系统出现大量线程,会对系统性能有影响;

Java使用的就是一对一线程模型,因此在Java中启一个线程要谨慎。

多对多模型

thread model

多对多模型,又叫做两级线程模型,它是博采众长以后的产物,充分吸取前两种线程模型的优势且尽可能规避它们的缺点。

在此模型下,用户线程与内核线程是多对多(m : n,一般m>=n)的映射模型。

首先,区别于多对一模型,多对多模型中的一个进程能够与多个内核线程关联,因而进程内的多个用户线程能够绑定不一样的内核线程,这点和一对一模型类似;

其次,又区别于一对一模型,它的进程里的全部用户线程并不与内核线程一一绑定,而是能够动态绑定内核线程, 当某个内核线程由于其绑定的用户线程的阻塞操做被内核调度让出CPU时,其关联的进程中其他用户线程能够从新与其余内核线程绑定运行。

因此,多对多模型既不是多对一模型那种彻底靠本身调度的也不是一对一模型彻底靠操做系统调度的,而是中间态(自身调度与系统调度协同工做),由于这种模型的高度复杂性,操做系统内核开发者通常不会使用,因此更多时候是做为第三方库的形式出现。

优势:

  • 兼具多对一模型的轻量;
  • 因为对应了多个内核线程,则一个用户线程阻塞时,其余用户线程仍然能够执行;
  • 因为对应了多个内核线程,则能够实现较完整的调度、优先级等;

缺点:

  • 实现复杂【本篇文章由公众号“彤哥读源码”原创】;

Go语言中的goroutine调度器就是采用的这种实现方案,在Go语言中一个进程能够启动成千上万个goroutine,这也是其出道以来就自带“高并发”光环的重要缘由。

后面讲到Java中的ForkJoinPool的时候,咱们会拿Go语言的PMG线程模型来对比讲解。

总结

(1)线程分为用户线程和内核线程;

(2)线程模型有多对一模型、一对一模型、多对多模型;

(3)操做系统通常只实现到一对一模型;

(4)Java使用的是一对一线程模型,因此它的一个线程对应于一个内核线程,调度彻底交给操做系统来处理;

(5)Go语言使用的是多对多线程模型,这也是其高并发的缘由,它的线程模型与Java中的ForkJoinPool很是相似;

(6)python的gevent使用的是多对一线程模型;

彩蛋

你所学过的语言都是使用的什么线程模型呢?

推荐阅读

一、死磕 java集合系列

二、死磕 java原子系列

三、死磕 java同步系列


欢迎关注个人公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一块儿畅游源码的海洋。

qrcode

相关文章
相关标签/搜索