用户_核心态

这里主要说明用户线程与内核线程的映射关系: 多个用户线程映射到内核线层上执行。线程的主要目的是对资源的寻址、计算等调度操作。

一. 两种线程

内核级线程

内核级别线程当中,线程的所有工作都由内核实现,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核会为每个内核线程维护上下文信息,调度也在内核基于线程架构的基础上进行。

用户级线程

用户级线程跑在应用程序中,应用程序可以随意自行创建线程。通常来说,应用程序由单线程开始运行,然后在该线程上可以按需派生出其他的子用户线程。

关系

那么用户线程和内核线程之间有什么关系呢?
线程区别于进程的本质是,线程是CPU调度的基本单位,但是它不是资源的分配单位,换句话说,就是线程间共享进程分配的资源,线程间的资源是共享的,这也是为什么我们需要注意线程间的数据同步问题。
那么这样一来,内核线程和用户线程之间的关系,就回到了这个最核心的一个词:调度之上。
以JVM为例,我们在Java中可以在一定限度上使用Thread来新建线程,然后去执行我们的操作。它自然而然地,是跑在用户线程之上的。线程的执行,本质上就是CPU跑代码(取指、间址、访存、执行等等),CPU的调度就是线程间的切换。
早期的计算机系统中,不支持内核级线程,只支持用户级线程,而CPU的时间片切换是以进程为维度的,于是进程对于内核来说,只能看到进程本身。其控制不能细化到CPU执行本身,那么这样一来,如果某个用户线程上的操作阻塞了App中的一个用户线程,可能会导致整个进程都被阻塞。
后来,有了内核级别的线程,该线程由内核来完成控制,内核通过调度器对线程进行调度,将任务映射到多个处理器之上,进行执行。这样一来,应用内的多个线程能够再次映射到内核级别的线程上去执行。这样一来,操作系统对线程的管理就更加地灵活,同时,应用也不再需要自己去设计线程的调度算法。
结论就是,用户线程的执行是依赖于内核线程的,用户线程在用户态创建,但是最终都需要映射到内核级别的线程之上去运行。

二. 内核模型映射关系

多对一模型

即多个用户线程映射到一个内核线程之上。
这就是上文所述的“早期计算机系统”中的情况,用户线程对内核不可见,是编写应用的程序员自己管理、调度的。效率高,但是一个线程被阻塞,其他的线程也被阻塞了,因为操作系统只能看得到这一个内核线程,既然你在做IO,那么自然不会在程序内做CPU轮转,只能阻塞掉一整个进程。
途中的用户线程123都被映射到内核线程1之上,但是一旦其中一个发起IO请求,系统就只能看到内核线程1发起了IO请求,应用将无法轮转另外两个线程的执行,直到CPU重新处于就绪状态。

一对一模型

即一个用户线程映射到一个内核线程之上。
不会出现“多对一模型”的问题,但是每创建一个用户线程,就需要创建一个内核线程与之对应,开销比较大。

多对多模型

即多个用户线程会映射到多个内核线程之上,并且,用户线程的数量 >= 内核线程的数量。
这种情况下,内核线程属于是一个服务多个了,但是一个进程之内的多个进程可以映射到不同的内核线程之上,我们可能将IO和主线程分开,主线程执行一些重要的任务,不会被阻塞;而子线程作为IO线程,一些耗时的操作放在子线程,即使被阻塞,那么对用户体验影响也比较小。(大多的具有用户界面的可视化交互App都是这么干的)
算是一种折中的方法,即保留了控制的灵活性,又节省了资源。但是在不同的局部也会出现二者的问题,例如进程A中的线程2和线程3一旦其一被阻塞,就会影响到另一个,这也意味着内核线程只能在进程间共享,不能跨进程共享,否则你的线程被其他进程阻塞了都不知道。
而具体的用户线程与系统线程的映射关系是由应用开发者决定的。

线程库

前文提到了用户级线程和内核级别线程的映射关系和映射模型,我们知道了:

  • 用户级线程和内核级线程是对应的,用户级线程不会自己凭空跑起来,最终都会落到一个内核线程当中去执行。
  • 用户线程的创建依赖于App开发者(Java中指JVM的开发者),而系统线程的创建依赖于内核,并且各自管理两种线程。

既然用户线程需要用到内核线程,那么对应的平台(操作系统)就必须为我们提供相关的操作库或者接口等等,来实现用户线程管理方式的设计和编写。
各自的平台都有各自的Api,比如Window下的Java,使用的线程库就是Win Api,Unix类的系统下,使用的就是PThread。
在JDK1.2版本中,Java自行开发了一套用户线程管理工具,名叫:绿色线程,实现方法我们不去细究,我们需要知道,它是用来做用户线程管理的一套方案即可,它解决的是用户线程和内核线程的映射。
绿色线程存在的问题,就是前面说到的,用户线程自行管理将无法被操作系统感知到,需要用户进行自行调度,无法更好地利用到当今CPU的多核心并行能力。
在JDK1.2之后,Java选择了更加稳定、方便的操作系统原生的内核级线程,直接通过系统调用,将线程的调度交还给了操作系统的内核,而不再去自己管理用户线程。这就是我们常说的,Java线程会直接对应到系统线程之上
JVM自身会屏蔽掉操作系统带来的一些差异,为上层的字节码执行提供稳定的运行环境,但是操作系统本身的种种差异也会导致一些不同,JVM的线程映射到系统线程之后,具体的情况需要由操作系统的线程模型定义,一个Java线程被阻塞了,不一定内核线程就被阻塞了,因为很可能在一些其他的操作系统中,提供了1:1之外的内存映射关系,可能内核线程被内核调度走了去处理其他事情,所以,Java的内核状态并不一定能完全代表当前系统内核的状态。

四. 线程池

这样一来,我们知道为什么会有线程池这东西的存在了,池化思想在计算机中非常常见,例如数据库的连接池、网络的连接池、AndroidRecyclerView的ViewHolder缓存池、Message的池化保存等等。本意是将不用的结构节省下来,而不是去新建一个。
但是,在Java中,线程的创建看似很简单,直接:new Thread即可创建,如果在Java层面上来看,只是一个对象的创建,它被分配在堆空间中,这似乎也不是特别耗费性能**,在1:1模型中,创建一个线程也对应创建一个轻量级进程**这就非常影响性能了。
线程池所做的,就是一个管理 + 调度功能,在不需要是将线程缓存起来,在需要时进行任务下发执行,Thread本身是CPU调度的单位,而Runnable就是Thread执行的基本单位,我们将一个Runnable交给线程池,由线程池帮助我们执行,执行完后将线程存储起来,而不是被GC回收掉。

作者:rEd_
链接:https://juejin.cn/post/7085227749215830023
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值