一、用户线程 && 内核线程
线程:一般分为 用户线程(ULT) 和 内核线程(KLT)。
用户线程:
用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态/内核态切换,速度快。内核对ULT无感知,线程阻塞则进程(包括它的所有线程)阻塞。
也就是说用户程序管理。
内核线程(KLT):
系统内核管理线程(KLT),内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,多线程在多处理器上并行运行。线程的创建、调度和管理由内核完成,效率比ULT要慢,比进程操作快。
也就是操作系统管理。
线程的实现方式?
线程的实现主要有三种方式:
- 使用内核线程(Kernel-Level Thread,KLT)实现(也叫1:1实现)。也就是直接使用操作系统内核支持的线程,这种方式的线程由内核完成线程切换,内核是通过操作系统的调度器来调度线程,并且将现成的任务映射到各个处理器上。
- 每一个内核线程可以认为是内核的一个分身,这样操作系统就可以同时处理多件事。
- 那么程序本身要通过内核线程的高级接口:LWP(Light Weight Process,轻量级进程),来使用内核线程。
- 因为基于内核线程,所以各种线程操作都要进行系统调用,系统调用需要再用户态和内核态来回切换,代价较高,另外每个轻量级进程(也就是程序使用的线程)要消耗内核资源,所以数量有限。
- 使用用户线程(User Thread,UT)实现(也叫1:N实现)。狭义上来说用户线程当然指完全建立在用户空间的线程库,系统内核感知不到他们的实现。这就代表实现会非常复杂,要程序去处理阻塞、映射、调度问题,甚至是不可能实现的,所以一般应用程序都不倾向使用用户线程。但是今年来以高并发为卖点的编程语言又普遍支持了,比如Golang。
- 使用用户线程+轻量级进程混合实现(也叫N:M实现)。许多 Unix 系列的操作系统提供了这种模型的实现。
JVM 用的是什么模型?
java的线程实现并不受 java 虚拟机规范的约束,这是一个和具体的虚拟机相关的话题,不过现在基本上市面的 jvm,用的都是基于操作系统原生的线程模型实现,也就是 1:1 的线程模型。 KLT 也就是内核线程。
二、操作系统的空间划分
操作系统会把地址空间,转化成一个 逻辑上的空间,基本上是划分成用户空间和内核空间。
这两个空间最大区别就是权限不同,目的就是为了防止用户去访问一些底层的东西,带来安全隐患。
(那么,我们的程序很多都是会操作硬件的,既然都不在一个空间,是怎么实现的呢?就是操作系统给用户提供的api,也就是上面我们以java用户线程实现为例说的LWP(Light Weight Process,轻量级进程),比如 linux 会有 p_thread ,让用户空间能创建内核线程)
内核线程与 java 的线程是 1:1的映射关系如下图所示: