一、相关知识
用户级线程(ULT):实现在用户空间的线程称为用户级线程。用户线程是完全建立在用户空间的线程库,用户线程的创建、调度、同步和销毁全由用户空间的库函数完成,不需要内核的参与,也不需要进行用户态和内核态的切换,因此这种线程的系统资源消耗非常低,且非常的高效。用户级线程调度器在用户空间的线程库实现,内核并不知道用户线程的存在,因此只有一个处理器内核会被分配给该进程 ,也就不能发挥多核 CPU 的优势,无法做到真正意义上的并发,且如果一个进程内部有一个线程发生了阻塞,会导致这个进程(包括它的所有线程)都阻塞。
内核级线程(KLT):内核线程建立和销毁都是由操作系统负责、通过系统调用完成的。在内核的支持下运行,无论是用户进程的线程,或者是系统进程的线程,他们的创建、撤销、切换都是依靠内核实现的,但是调度开销要比用户线程更大。此外线程表是存放在操作系统固定的表格空间或者堆栈空间里,所以内核级线程的数量是有限的。
二、Linux的线程模型
Linux线程又称为“轻量级进程”,因为在Linux中并没有专门的数据结构用于描述线程,只有task_struct这一种描述进程的结构体。在内核看来只有进程而没有线程,线程调度时也是当做进程来调度的。
- 一个Linux进程拥有自己独立的地址空间,而一个轻量级进程没有自己独立的地址空间,它只有一个最小的执行上下文和调度程序所需的统计信息,它只带有进程执行相关的信息,与父进程共享进程地址空间。因此LWP之间可以通过共享内存直接进行数据交换,无需复杂的IPC机制,通信效率更高。
- 轻量级进程简称LWP,是一种由内核支持的用户线程,每一个轻量级进程都与一个特定的内核线程关联。它是基于内核线程的高级抽象,系统只有先支持内核线程才能有 LWP。每一个进程有一个或多个 LWP,每个LWP 由一个内核线程支持,在这种实现的操作系统中 LWP 就是用户线程。
三、常见的线程模型
线程模型:线程模型是指操作系统或编程语言中管理线程执行方式的架构或策略。在同时支持用户级线程和内核级线程的操作系统中,可以采用两者结合的方式,将n个用户级线程映射到m个内核级线程上。
- 一对一:一个用户级线程对应一个内核级线程,即创建一个用户级线程就需要创建一个对应的内核级线程。当进程中的一个线程阻塞时,其他线程仍然可以继续执行,并发能力强。然而一个用户进程会占用多个内核级线程,线程调度需要由操作系统内核完成,涉及用户态和内核态的切换,开销较大。
- 多对一:多个用户及线程对应一个内核级线程,每个用户进程只对应一个内核级线程。用户级线程的切换在用户态完成即可,不需要切换到内核态,线程管理的开销小。然而当一个用户线程被阻塞时,整个进程中的所有线程都会被阻塞,无法利用多CPU的并发优势。
- 多对多:n个用户级线程对应m个内核级线程,结合了用户级线程和内核级线程的优点,克服了一对一模型中用户进程占用太多内核级线程,且所有线程切换都需要在内核态进行的开销,又克服了多对一模型中无法支持并发的缺点。
四、Java线程模型
Java程序在JVM上运行,JVM可以运行在不同的操作系统之上,不同的操作系统创建线程的方式是不同的,JVM通过对不同操作系统的原生线程进行抽象,屏蔽了底层不同操作系统的实现方式。而JVM线程和操作系统线程的映射关系即Java的线程模型,其约定了用户线程和操作系统线程的规范和协议。不同的JVM实现中使用了不同的线程模型。
- 目前主流的JVM采用的都是一对一的线程模型,即一个JAVA线程会映射到一个内核级线程,每个线程都是独立的调度单元,直接利用操作系统内核提供的调度功能。
- Java早期版本采用过多对一的线程模型,Java 线程在 Solaris 系统上的初始实现是多对一的
- Go语言采用的GMP线程模型就是基于多对多的方式来实现的,这也是为什么能够利用goroutine实现更高并发的原因。值得一提的是,Java的Loom项目也在进行这方面的探索。
参阅:
Multithreading Models (JDK 1.1 for Solaris Developer's Guide) (oracle.com)
Java 线程模型 - jqc - 博客园 (cnblogs.com)
干货 | 进程、线程、协程 10 张图讲明白了! - 知乎 (zhihu.com)
Java并发系列之一:JVM线程模型-CSDN博客