专栏首页彤哥读源码死磕 java线程系列之线程模型

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

问题

(1)线程类型有哪些?

(2)线程模型有哪些?

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

简介

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

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

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

多对一模型

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

优点:

  • 用户线程的很多操作对内核来说都是透明的,不需要用户态和内核态的频繁切换,使线程的创建、调度、同步等非常快;

缺点:

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

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

一对一模型

一对一模型,又叫作内核级线程模型,即一个用户线程对应一个内核线程,内核负责每个线程的调度,可以调度到其他处理器上面。

优点:

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

缺点:

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

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

多对多模型

多对多模型,又叫作两级线程模型,它是博采众长之后的产物,充分吸收前两种线程模型的优点且尽量规避它们的缺点。

在此模型下,用户线程与内核线程是多对多(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使用的是多对一线程模型;

本文分享自微信公众号 - 彤哥读源码(gh_63d1b83b9e01),作者:唐彤

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 死磕 java线程系列之自己动手写一个线程池

    线程池是Java并发编程中经常使用到的技术,那么自己如何动手写一个线程池呢?本文彤哥将手把手带你写一个可用的线程池。

    彤哥
  • 死磕 java线程系列之线程池深入解析——生命周期

    上一章我们一起重温了下线程的生命周期(六种状态还记得不?),但是你知不知道其实线程池也是有生命周期的呢?!

    彤哥
  • 死磕 java同步系列之开篇

    同步系列,这是彤哥想了好久的名字,本来是准备写锁相关的内容,但是java中的CountDownLatch、Semaphore、CyclicBarrier这些类又...

    彤哥
  • 死磕 java线程系列之线程模型

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

    黄泽杰
  • python多线程开发

           通过threading.Thread创建一个线程对象,target是目标函数,name可以指定自己喜欢的名字,线程的启动需要借助start方法。线...

    用户2398817
  • java.util.concurrent 在shorturl项目中的应用

    问题:微博短链项目应用到哪些concurrent包中的类,类的用途是什么?场景是怎样?效果? java.util.concurrent.atomic.Atomi...

    财主刀刀
  • java多线程中的死锁、活锁、饥饿、无锁都是什么鬼?

    死锁、活锁、饥饿是关于多线程是否活跃出现的运行阻塞障碍问题,如果线程出现了这三种情况,即线程不再活跃,不能再正常地执行下去了。 死锁 死锁是多线程中最差的一种情...

    Java技术栈
  • 想进大厂?50个多线程面试题,你会多少?(一)

    最近看到网上流传着,各种面试经验及面试题,往往都是一大堆技术题目贴上去,而没有答案。

    搜云库
  • Java锁---偏向锁、轻量级锁、自旋锁、重量级锁

    之前做过一个测试,反复执行过多次,发现结果是一样的:  1. 单线程下synchronized效率最高(当时感觉它的效率应该是最差才对);  2. Atom...

    stys35
  • 深入理解JVM(线程部分) Note

    由于计算机的存储设备与处理器的运算速度有几个数量级的差距,所以现代计算机系统都不得不加入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与...

    六月的雨

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动