1.基础概念:
1.1. 进程、轻量级进程、内核线程、用户线程的关系和区别
在现代操作系统中,进程支持多线程。
进程是资源管理的最小单元。一个进程内部可以包含多个线程。这些线程会共享进程的资源。
而线程是程序执行的最小单元。线程必须依赖进程而存在。
一个进程的组成实体可以分为两大部分:线程集合资源集。资源集包括地址空间、打开的文件、用户信息等等,由进程内的线程共享。
线程有自己的私有数据:程序计数器,栈空间以及寄存器。
2. 为什么使用多线程模型:
- 现实中有很多需要并发处理的任务,如数据库的服务器端、网络服务器、大容量计算等。
- 传统的UNIX进程是单线程的,单线程意味着程序必须是顺序执行,不能并发;既在一个时刻只能运行在一个处理器上,因此不能充分利用多处理器框架的计算机。
- 如果采用多进程的方法,则有如下问题:a. fork一个子进程的消耗是很大的,fork是一个昂贵的系统调用,即使使用现代的写时复制(copy-on-write)技术。b. 各个进程拥有自己独立的地址空间,进程间的协作需要复杂的IPC技术,如消息传递和共享内存等。
3. 多线程的优缺点
多线程的优点和缺点实际上是对立统一的。
支持多线程的程序(进程)可以取得真正的并行(parallelism),且由于共享进程的代码和全局数据,故线程间的通信是方便的。它的缺点也是由于线程共享进程的地址空间,因此可能会导致竞争,因此对某一块有多个线程要访问的数据需要一些同步技术。
4. 三种线程——内核线程、轻量级进程、用户线程
4.1. 内核线程(KLT)
内核线程就是内核的分身,一个分身可以处理一件特定事情。这在处理异步事件(如异步IO)时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。
- CPU时间片轮转机制:又称RR调度,会导致上下文切换。
- CPU核心数和内核级线程数的关系
一般情况下一个内核能同时运行一个内核级线程。使用了超线程技术后一个内核能同时运行2个内核级线程。
当内核级线程数小于CPU内核数或者超过CPU内核数的2倍。CPU的运行效率都会受到较大的影响。对于计算密集型线程,线程数一般在这个范围之内。
对于计算密集型的任务,一般建议将线程数设置为物理核数。具体的,还需要针对不同的程序,做对应压力测试得到合适的参数选择。
4.2. 轻量级进程(LWP)
轻量级线程(LWP)是一种由内核支持的用户线程。它是基于内核线程的高级抽象,因此只有先支持内核线程,才能有LWP。每一个进程有一个或多个LWPs,每个LWP由一个内核线程支持。这种模型实际上就是恐龙书上所提到的一对一线程模型。在这种实现的操作系统中,LWP就是用户线程。
由于每个LWP都与一个特定的内核线程关联,因此每个LWP都是一个独立的线程调度单元。即使有一个LWP在系统调用中阻塞,也不会影响整个进程的执行。
轻量级进程具有局限性。首先,大多数LWP的操作,如建立、析构以及同步,都需要进行系统调用。系统调用的代价相对较高:需要在user mode和kernel mode中切换。其次,每个LWP都需要有一个内核线程支持,因此LWP要消耗内核资源(内核线程的栈空间)。因此一个系统不能支持大量的LWP。
image.png
注:
1 LWP的术语是借自于SVR4/MP和Solaris 2.x。
2 有些系统将LWP称为虚拟处理器。
3 将之称为轻量级进程的原因可能是:在内核线程的支持下,LWP是独立的调度单元,就像普通的进程一样。所以LWP的最大特点还是每个LWP都有一个内核线程支持。
4.3. 用户线程(ULT)
LWP虽然本质上属于用户线程,但LWP线程库是建立在内核之上的,LWP的许多操作都要进行系统调用,因此效率不高。而这里的用户线程指的是完全建立在用户空间的线程库,用户线程的建立,同步,销毁,调度完全在用户空间完成,不需要内核的帮助。因此这种线程的操作是极其快速的且低消耗的。
image.png
上图是最初的一个用户线程模型,从中可以看出,进程中包含线程,用户线程在用户空间中实现,内核并没有直接对用户线程进行调度,内核的调度对象和传统进程一样,还是进程本身,内核并不知道用户线程的存在。用户线程之间的调度由在用户空间实现的线程库实现。
这种模型对应着恐龙书中提到的多对一线程模型。其缺点是:多核处理器下,同一个进程中的多个用户线程只能分时复用同一个内核(同一个进程中的多个用户线程只能同时有一个线程在运行)。一个用户线程如果阻塞在系统调用中,则整个进程都将会阻塞。执行效率低。
4.4. 加强版的用户线程——用户线程+LWP
这种模型对应着恐龙书中多对多模型。用户线程库还是完全建立在用户空间中,因此用户线程的操作还是很廉价,因此可以建立任意多需要的用户线程。操作系统提供了LWP作为用户线程和内核线程之间的桥梁。LWP还是和前面提到的一样,具有内核线程支持,是内核的调度单元,并且用户线程的系统调用要通过LWP,因此进程中某个用户线程的阻塞不会影响整个进程的执行。用户线程库将建立的用户线程关联到LWP上,LWP与用户线程的数量不一定一致。当内核调度到某个LWP上时,此时与该LWP关联的用户线程就被执行。
image.png
5. JVM线程
JAVA 天生就是多线程的。
JDK1.2之前,绿色线程——用户线程。
JDK1.2以后,JVM线程基于操作系统原生线程模型来实现。JDK的Windows版本和Linux版本都使用一对一的线程模型实现。JVM线程与LWP一一对应。
一个大型应用程序,我们可以开辟的线程数量至少等于运行机器的cpu内核数量。java程序里我们可以通过下面的一行代码得到这个数量:
Runtime.getRuntime().availableProcessors();
所以最小线程数量即时cpu内核数量。如果所有的任务都是计算密集型的,这个最小线程数量就是我们需要的线程数。开辟更多的线程只会影响程序的性能,因为线程之间的切换工作,会消耗额外的资源。如果任务是IO密集型的任务,我们可以开辟更多的线程执行任务。当一个任务执行IO操作的时候,线程将会被阻塞,处理器立刻会切换到另外一个合适的线程去执行。如果我们只拥有与内核数量一样多的线程,即使我们有任务要执行,他们也不能执行,因为处理器没有可以用来调度的线程。
轻量级线程(LWP)是基于内核线程的高级抽象,因此只有先支持内核线程,才能有LWP。每一个进程有一个或多个LWPs,每个LWP由一个内核线程支持。这种模型实际上就是恐龙书上所提到的一对一线程模型。在这种实现的操作系统中,LWP就是用户线程。
网友评论