目录
[协同式(Cooperative Threads-Scheduling)线程调度](#协同式(Cooperative Threads-Scheduling)线程调度)
[抢占式(Preemptive Threads-Scheduling)线程调度](#抢占式(Preemptive Threads-Scheduling)线程调度)
进程与线程
进程是资源分配与调度的基本单位 ,线程是任务执行的基本单位。
一个进程中可以有多个线程,线程之间共享这个进程的资源与代码空间,线程之间切换只需要切换线程的程序寄存器和堆栈。寄存器数据直接存储在 CPU 的硬件寄存器中,当线程操作结束后,操作系统会将其寄存器状态(线程上下文)保存到内核空间的线程控制块(TCB,Thread Control Block) 中。
进程运行时,CPU 寄存器(如指令指针、通用寄存器、状态寄存器等)存储实时执行状态。当进程被暂停(如调度切换、中断)时,这些寄存器的值会被保存到PCB 中专门的上下文存储区域(属于内核空间)。PCB 包含:
- 进程 ID(PID)、父进程 ID(PPID)等标识信息;
 - 进程状态(运行、就绪、阻塞等);
 - 调度信息(优先级、时间片等);
 - 内存管理信息(页表指针、地址空间边界等);
 - 文件描述符表、信号处理表等资源信息。
 
线程的实现
内核线程
使用内核线程实现的方式也被称为1:1实现。内核线程(Kernel-LevelThread,KLT)就是直接由操作系统内核(Kernel,下称内核)支持的线程,这种线程由内核来完成线程切换,内核通过**操纵调度器(Scheduler)**对线程进行调度,并负责将线程的任务映射到各个处理器上。
程序一般不会直接使用内核线程,而是内核线程另一种的实现:**轻量级进程(Light Weight Process,LWP)。**轻量级进程就是通常意义上所讲的线程,由于每个轻量级进程都由一个内核线程支持,因此只有先支持内核线程,才能有轻量级进程。它结合了进程和线程的部分特性,旨在提供更高效的并发能力。

用户线程
使用用户线程实现的方式被称为1:N实现。一个线程只要不是内核线程,都可以认为是用户线程(User Thread,UT)的一种。
用户线程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助。如果程序实现得当,这种线程不需要切换到内核态,因此操作可以是非常快速且低消耗的,也能够支持规模更大的线程数量。

混合实现
将内核线程与用户线程一起使用的实现方式,被称为N:M实现。
用户线程还是完全建立在用户空间中,因此用户线程的操作依然廉价,并且可以支持大规模的用户线程并发。而操作系统支持的轻量级进程则作为用户线程和内核线程之间的桥梁,这样可以使用内核提供的线程调度功能及处理器映射,并且用户线程的系统调用要通过轻量级进程来完成,这大大降低了整个进程被完全阻塞的风险。
Java的实现
以HotSpot为例,它的每一个Java线程都是直接映射到一个操作系统原生线程来实现的,而且中间没有额外的间接结构,所以HotSpot自己是不会去干涉线程调度的(可以设置线程优先级给操作系统提供调度建议),全权交给底下的操作系统去处理,所以何时冻结或唤醒线程、该给线程分配多少处理器执行时间、该把线程安排给哪个处理器核心去执行等,都是由操作系统完成的,也都是由操作系统全权决定的。
线程调度
协同式(Cooperative Threads-Scheduling)线程调度
线程的执行时间由线程本身来控制,线程把自己的工作执行完了之后,要主动通知系统切换到另外一个线程上去。
缺点:
线程执行时间不可控,如果一个线程出现故障则会一直阻塞。
抢占式(Preemptive Threads-Scheduling)线程调度
每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定。
状态转换

新建(New):创建后尚未启动的线程处于这种状态。
运行(Runnable):包括操作系统线程状态中的Running和Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着操作系统为它分配执行时间。
无限期等待(Waiting):处于这种状态的线程不会被分配处理器执行时间,它们要等待被其他线程显式唤醒。
限期等待(Timed Waiting):处于这种状态的线程也不会被分配处理器执行时间,不过无须等待被其他线程显式唤醒,在一定时间之后它们会由系统自动唤醒。
阻塞(Blocked):线程被阻塞了。
结束(Terminated):已终止线程的线程状态,线程已经结束执行。
协程
内核线程的局限
内核线程的调度成本主要来自于用户态与核心态之间的状态转换,而这两种状态转换的开销主要来自于响应中断、保护和恢复执行现场的成本。
1:1的内核线程模型是如今Java虚拟机线程实现的主流选择,但是这种映射到操作系统上的线程天然的缺陷是切换、调度成本高昂,系统能容纳的线程数量也很有限。在每个请求本身的执行时间变得很短、数量变得很多的前提下,用户线程切换的开销甚至可能会接近用于计算本身的开销,这就会造成严重的浪费。
协程的优势
虽然协程无法避免上下文切换之间的开销,但是,一旦把保护、恢复现场及调度的工作从操作系统交到程序员手上,就会有很多方法来缩减这些开销。
协程分为有栈协程(Stackfull Coroutine) 和无栈协程(Stackless Coroutine)。
协程的主要优势是轻量,无论是有栈协程还是无栈协程,都要比传统内核线程要轻量得多。