线程的实现原理
0、 概述
操作系统引入线程之后,进程就变成了分配资源的基本单位,线程是独立调度的基本单位。
引入线程的意义:
未引入线程之前,进程是分配资源和独立调度的基本单位,因而在创建、撤销、切换中,系统必须为之付出较大的时空开销。正因如此,在系统中所设置的进程,其数目不宜过多,进程切换的频率也不宜过高,这也就限制这并发程度的进一步提高。
那么如何能使多个程序更好的并发执行同时又尽量减少系统的开销?不少学者发现:能不能将进程的上述两个属性分开,由操作系统分开处理。即对于作为调度的基本单位,不同时作为拥有资源的单位,以做到"轻装上阵";而对于拥有资源的基本单位,又不对其进行频繁的切换。
正是在这种思想的指导下,形成了线程的概念。
实现线程的方式主要有三种:
- 使用内核线程实现(1:1)
- 使用用户线程实现(1:N)
- 使用用户线程+轻量级进程混合实现(N:M)
1、内核线程的实现
内核线程(KTL)就是直接由操作系统内核直接支持的线程这种吸纳从由内核来完成线程的切换,内核通过线程调度器完成对线程的调度,并负责将线程的任务映射到各个CPU或者核心上。
程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口——轻量级进程(LWP),轻量级进程就是我们通常意义上的线程,每个轻量级进程都有一个内核线程支持,轻量级进程是用户线程和内核线程之间的桥梁。因此只有先支持内核线程,才能有轻量级进程。这种轻量级进程与内核线程1:1映射。
![](https://img2020.cnblogs.com/blog/1610225/202105/1610225-20210528141038882-617285904.png)
优越性:
- 由于内核线程的支持,每个轻量级进程都成为一个独立的调度单元,即使其中某一个轻量级进程阻塞,也不会影响整个进程的进度。
局限性:
- 这种方式基于内核线程实现,需要进行系统调用,操作系统在管态目态频繁切换,造成的开销较大;
- 每个轻量级进程都需要有一个内核线程支持,因此轻量级进程消耗一定的内核资源,因此一个系统支持的轻量级进程数量有限。
2、用户线程实现
广义上认为只要一个线程不是内核线程,都可以认为是用户线程(UT),因此从这个角度看来轻量级进程也属于用户线程,但是轻量级进程的实现始终依赖于内核线程,许多操作需要进行系统调用,因此会限制到效率。
![](https://img2020.cnblogs.com/blog/1610225/202105/1610225-20210528141102188-1392446143.png)
侠义上的用户线程指的是完全建立在用户空间上的线程库中,系统内核不能感受到用户线程的存在以及如何实现的。用户线程的建立、同步、销毁以及调度完全是在用户态完成的,无需内核的支持。如果程序的实现策略得当,这种线程不需要切换到内核态,因此可以以非常低廉的代价支持大规模的线程。
优越性:
- 无需系统内核的支持,所有的线程需要用户程序自行处理;
局限性:
- 没有系统内核的支持,线程的创建、切换和调度都由用户自行考虑。诸如阻塞如何处理、多处理器系统中如何将线程映射到其它处理器上的问题,解决起来相当繁琐;
3、混合实现
在混合实现下,既存在用户线程,也存在轻量级进程。用户线程还是完全建立在用户空间中,因此用户线程的创建、切换等操作开销比较小,可以支撑大规模的用户线程并发。轻量级进程作为用户线程和内核线程之间的桥梁,可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,这样大大降低了整个进程阻塞的风险。在这种混合模式下,用户线程与轻量级进程的数量比是不恒定的,是N:M的关系。
![](https://img2020.cnblogs.com/blog/1610225/202105/1610225-20210528141119984-1178539636.png)
4、Java线程的实现
Java进程如何实现不受Java虚拟机规范的约束,与具体的虚拟机相关。在HotSpot中,每一个Java线程都是直接映射到一个操作系统原生线程实现,而且中间没有额外的间接结构。所以HotSpot虚拟机不会自己去干涉线程的调度,全部交给操作系统去处理,何时冻结或者唤醒线程,该给线程分配多少处理器执行时间、该八线程交给哪个处理器的哪个核心去执行等,都是由操纵系统决定的。