从 Linux 内核线程反观 Java Go 的线程模型

简介: > 原文参考我的个人公众号文章(欢迎关注!):[点此链接进入](https://mp.weixin.qq.com/s?__biz=MzkxNDMyNjk0Mw==&mid=2247484374&idx=1&sn=5e5c8ef7adc0841019cb3302ed5003db&chksm=c1715726f606de301494abf87fbdc40303bdb9ac8393333b0778e23
原文参考我的个人公众号文章(欢迎关注!): 点此链接进入

为什么要从 Linux内核线程角度出发去分析开发语言的线程模型?

这里且不说 Linux 为什么是流行的操作系统,每当提到这个话题,因为Linux开源、稳定等等,这里不说那么多题外话。之所以从 Linux 内核角度出发,是因为操作系统是每个热衷于技术的程序员心之所向的地方,并且很多开发语言都或多或少对操作系统有依赖。

为什么本文选择 Java 和 Go 来分析其线程模型?

所有与 IT 相关的科班专业,如计算机科学与技术、网络工程等等,必修 C 语言,C 语言是最贴近硬件的语言,是很多操作系统的开发语言。也正因为如此 C 语言对与开发人员来说并不是那么友好,比如组织大型的项目,比如融入更多的设计模式等。Java 和 Go 抛开目前作为主流开发语言的原因,Java 和 Go 都在高并发编程层面有各自的建树,尤其是 Go 语言号称是专为并发而生的开发语言。

Linux 内核线程

内核线程是直接有内核本身启动的进程。内核线程实际上是将内核函数委托给独立的进程,与其他进程是并行执行的(同时也并行与内核自身的执行)。内核线程经常被称为(内核)守护进程。
内核进程的两个特点:

  1. 他们在cpu的管理态执行(supervisor mode),而并不是用户态
  2. 他们只可以访问虚拟地址空间的内核部分,但不能访问用户空间

内核线程其他的细节这里就暂不细说,只需要知道其两个特点我们就可以进一步分析线程的实现,如果感兴趣可以就虚拟地址空间的内核部分、以及针对下面线程实现方式中 用户态 与 内核态 切换 去进一步探究(这里就不细说了,默认大家都知道,就不老生常谈了)。

线程的三种实现方式

  1. 使用内核线程实现,用户线程数和内核线程数为1:1
  2. 使用用户线程实现,用户线程数和内核线程数为N:1,每个用户线程对应一个轻量级线程(内核线程高级接口:轻量级线程LWP)
  3. 使用用户线程+轻量级线程实现,用户线程和轻量级线程为N:M
  • 内核实现

  • 用户线程实现

  • 混合实现

Java 线程模型

Java 线程的实现不受 Java 虚拟机规范的约束,在早期 JVM 上(JDK1.2以前),通过一种“绿色线程”(Green Threads)的用户线程实现的,但从 JDK1.3 开始,主流商用虚拟机模型普遍采用操作系统与安生线程模型来实现 ,即采用操作系统内核线程实现。
以HotSpot为例,它的每一个Java线程都是直接映射到一个操作系统原生线程来实现的,而且中间没有额外的间接结构,所以HotSpot自己是不会去干涉线程调度的(可以设置线程优先级给操作系统提供调度建议),全权交给底下的操作系统去处理,所以何时冻结或唤醒线程、该给线程分配多少处理器执行时间、该把线程安排给哪个处理器核心去执行等,都是由操作系统完成的,也都是由操作系统全权决定的。

Go 线程模型

Go 作为专为并发而生的语言,当然有其独有的东西:两级线程模型。

两级线程模型


用户线程与 内核线程 为多对多(N:M)的映射关系。两级线程模型吸收前两种线程模型的优点并且尽量规避了它们的缺点,区别于用户级线程模型,两级线程模型中的进程可以与多个内核线程 关联,也就是说一个进程内的多个线程可以分别绑定一个自己的 内核线程,这点和内核级线程模型相似;其次,又区别于内核级线程模型,它的进程里的线程并不与内核线程 唯一绑定,而是可以多个用户线程映射到同一个 内核线程,当某个 内核线程 因为其绑定的线程的阻塞操作被内核调度出 CPU 时,其关联的进程中其余用户线程可以重新与其他 内核线程 绑定运行。所以,两级线程模型既不是用户级线程模型那种完全靠自己调度的也不是内核级线程模型完全靠操作系统调度的,而是一种自身调度与系统调度协同工作的中间态,即用户调度器实现用户线程到 内核线程 的调度,内核调度器实现 内核线程 到 CPU 上的调度。

Go 特有的两级线程模型 GMP

由 Go 调度器实现 Goroutine 到 KSE 的调度,由内核调度器实现 KSE 到 CPU 上的调度。Go 的调度器使用 G、M、P 三个结构体来实现 Goroutine 的调度,也称之为GMP 模型。
G:表示 Goroutine。每个 Goroutine 对应一个 G 结构体,G 存储 Goroutine 的运行堆栈、状态以及任务函数,可重用。当 Goroutine 被调离 CPU 时,调度器代码负责把 CPU 寄存器的值保存在 G 对象的成员变量之中,当 Goroutine 被调度起来运行时,调度器代码又负责把 G 对象的成员变量所保存的寄存器的值恢复到 CPU 的寄存器
M:OS 底层线程的抽象,它本身就与一个内核线程进行绑定,每个工作线程都有唯一的一个 M 结构体的实例对象与之对应,它代表着真正执行计算的资源,由操作系统的调度器调度和管理。M 结构体对象除了记录着工作线程的诸如栈的起止位置、当前正在执行的 Goroutine 以及是否空闲等等状态信息之外,还通过指针维持着与 P 结构体的实例对象之间的绑定关系
P:表示逻辑处理器。对 G 来说,P 相当于 CPU 核,G 只有绑定到 P(在 P 的 local runq 中)才能被调度。对 M 来说,P 提供了相关的执行环境(Context),如内存分配状态(mcache),任务队列(G)等。它维护一个局部 Goroutine 可运行 G 队列,工作线程优先使用自己的局部运行队列,只有必要时才会去访问全局运行队列,这可以大大减少锁冲突,提高工作线程的并发性,并且可以良好的运用程序的局部性原理
一个 G 的执行需要 P 和 M 的支持。一个 M 在与一个 P 关联之后,就形成了一个有效的 G 运行环境(内核线程+上下文)。每个 P 都包含一个可运行的 G 的队列(runq)。该队列中的 G 会被依次传递给与本地 P 关联的 M,并获得运行时机。
M 与 KSE 之间总是一一对应的关系,一个 M 仅能代表一个内核线程。M 与 KSE 之间的关联非常稳固,一个 M 在其生命周期内,会且仅会与一个 KSE 产生关联,而 M 与 P、P 与 G 之间的关联都是可变的,M 与 P 也是一对一的关系,P 与 G 则是一对多的关系。

目录
相关文章
|
3月前
|
安全 网络协议 Linux
深入理解Linux内核模块:加载机制、参数传递与实战开发
本文深入解析了Linux内核模块的加载机制、参数传递方式及实战开发技巧。内容涵盖模块基础概念、加载与卸载流程、生命周期管理、参数配置方法,并通过“Hello World”模块和字符设备驱动实例,带领读者逐步掌握模块开发技能。同时,介绍了调试手段、常见问题排查、开发规范及高级特性,如内核线程、模块间通信与性能优化策略。适合希望深入理解Linux内核机制、提升系统编程能力的技术人员阅读与实践。
410 1
|
3月前
|
Ubuntu Linux
Ubuntu 23.04 用上 Linux 6.2 内核,预计下放到 22.04 LTS 版本
Linux 6.2 带来了多项内容更新,修复了 AMD 锐龙处理器设备在启用 fTPM 后的运行卡顿问题,还增强了文件系统。
|
3月前
|
Ubuntu Linux
Ubuntu 23.10 现在由Linux内核6.3提供支持
如果你想在你的个人电脑上测试一下Ubuntu 23.10的最新开发快照,你可以从官方下载服务器下载最新的每日构建ISO。然而,请记住,这是一个预发布版本,所以不要在生产机器上使用或安装它。
|
1月前
|
Java 大数据 Go
从混沌到秩序:Java共享内存模型如何通过显式约束驯服并发?
并发编程旨在混乱中建立秩序。本文对比Java共享内存模型与Golang消息传递模型,剖析显式同步与隐式因果的哲学差异,揭示happens-before等机制如何保障内存可见性与数据一致性,展现两大范式的深层分野。(238字)
62 4
|
3月前
|
缓存 前端开发 Java
Java类加载机制与双亲委派模型
本文深入解析Java类加载机制,涵盖类加载过程、类加载器、双亲委派模型、自定义类加载器及实战应用,帮助开发者理解JVM核心原理与实际运用。
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
Java 大视界 -- Java 大数据机器学习模型在自然语言生成中的可控性研究与应用(229)
本文深入探讨Java大数据与机器学习在自然语言生成(NLG)中的可控性研究,分析当前生成模型面临的“失控”挑战,如数据噪声、标注偏差及黑盒模型信任问题,提出Java技术在数据清洗、异构框架融合与生态工具链中的关键作用。通过条件注入、强化学习与模型融合等策略,实现文本生成的精准控制,并结合网易新闻与蚂蚁集团的实战案例,展示Java在提升生成效率与合规性方面的卓越能力,为金融、法律等强监管领域提供技术参考。
|
3月前
|
机器学习/深度学习 算法 Java
Java 大视界 -- Java 大数据机器学习模型在生物信息学基因功能预测中的优化与应用(223)
本文探讨了Java大数据与机器学习模型在生物信息学中基因功能预测的优化与应用。通过高效的数据处理能力和智能算法,提升基因功能预测的准确性与效率,助力医学与农业发展。
|
3月前
|
机器学习/深度学习 搜索推荐 数据可视化
Java 大视界 -- Java 大数据机器学习模型在电商用户流失预测与留存策略制定中的应用(217)
本文探讨 Java 大数据与机器学习在电商用户流失预测与留存策略中的应用。通过构建高精度预测模型与动态分层策略,助力企业提前识别流失用户、精准触达,实现用户留存率与商业价值双提升,为电商应对用户流失提供技术新思路。
|
3月前
|
消息中间件 人工智能 缓存
Go与Java Go和Java微观对比
本文对比了Go语言与Java在线程实现上的差异。Go通过Goroutines实现并发,使用`go`关键字启动;而Java则通过`Thread`类开启线程。两者在通信机制上也有所不同:Java依赖共享内存和同步机制,如`synchronized`、`Lock`及并发工具类,而Go采用CSP模型,通过Channel进行线程间通信。此外,文章还介绍了Go中使用Channel和互斥锁解决并发安全问题的示例。
228 0
下一篇
oss云网关配置