【Java并发编程】操作系统基础(一):进程与线程&线程模型

1.进程与线程

1.1 进程

资源分配单位。创建慢,上下文切换开销大

进程状态

  • 从上图可以看出,进程运行中只有三种状态:ready,running,waiting
  • 事实上还存在进程挂起状态,原因是为了满足CPU的某些需求,会将一些处于阻塞/就绪的进出交换出内存
    详细可以参考这篇博客
    img

1.2 线程(轻量级进程)

CPU调度单位。线程间共享进程资源。

在Linux下其实本并没有线程,只是为了迎合开发者口味,搞了个轻量级进程出来就叫做了线程。轻量级进程和进程一样,都有自己独立的task_struct进程描述符,也都有自己独立的pid。从操作系统视角看,调度上和进程没有什么区别

##JVM线程状态##

1.3 进程状态与线程状态间关系

  • Java虚拟机中的线程状态,不反应任何操作系统线程状态
  • 操作系统的线程也有它自己的状态。你Java有6种,Windows可能有N种,到了Linux系统,它可能有M种,加上各种操作系统版本满天乱飞,我也不知道具体有多少种了……
  • 但是!!虚拟机层的存在,统一了这些差别,不管它是N还是M种,到了Java层面它们都被映射到了6种状态上来。因此,两个层面上有很多状态其实是大同小异的。
    这里写图片描述

2.线程分类

2.1 内核线程

  • 内核线程就是直接由操作系统内核(Kernel)支持的线程,这种线程由内核来完成线程切换,内核通过操纵调度器(Scheduler)对线程进行调度,并负责将线程的任务映射到各个处理器上。每个内核线程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情,支持多线程的内核就叫做多线程内核(Multi-Threads Kernel)。
    内核线程切换由内核控制,当线程进行切换的时候,由用户态转化为内核态。切换完毕要从内核态返回用户态,即存在用户态和内核态之间的转换,比如多核cpu,还有win线程的实现。
  • 通俗的讲就是,程序员直接使用操作系统中已经实现的线程,而线程的创建、销毁、调度和维护,都是靠操作系统(准确的说是内核)来实现,程序员只需要使用系统调用,而不需要自己设计线程的调度算法和线程对CPU资源的抢占使用。

另外还有一个概念,轻量级进程(LWP:light weight process):是建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联。内核线程只能由内核管理并像普通进程一样被调度。

优点

  • 在多处理器系统中,内核能够同时调度同一进程中多个线程并行执行到多个处理器中;
  • 如果进程中的一个线程被阻塞,内核可以调度同一个进程中的另一个线程;
  • 内核支持线程具有很小的数据结构和堆栈,线程的切换比较快,切换开销小;
  • 内核本身也可以使用多线程的方式来实现。

缺点

即使CPU在同一个进程的多个线程之间切换,也需要陷入内核,因此其速度和效率不如用户级线程。

2.2 用户线程

  • 当线程在用户空间下实现时,操作系统对线程的存在一无所知,操作系统只能看到进程,而不能看到线程。所有的线程都是在用户空间实现。在操作系统看来,每一个进程只有一个线程。过去的操作系统大部分是这种实现方式,这种方式的好处之一就是即使操作系统不支持线程,也可以通过库函数来支持线程。
  • 换一种通俗的方式来讲解这段话,首先就是在这在模型下,程序员需要自己实现线程的数据结构、创建销毁和调度维护。也就相当于需要实现一个自己的线程调度内核,而同时这些线程运行在操作系统的一个进程内,最后操作系统直接对进程进行调度。
    在这里插入图片描述

优点:

  • 线程的切换无需陷入内核,故切换开销小,速度非常快;
  • 允许每个线程定制自己的调度算法,线程管理比较灵活。这就是必须自己写管理程序,与内核线程的区别

缺点:

  • 系统调用的阻塞问题:对应用程序来讲,同一进程中只能同时有一个线程在运行,一个线程的阻塞将导致整个进程中所有线程的阻塞
  • 由于这里的处理器时间片分配是以进程为基本单位,所以每个线程执行的时间相对减少。

2.3 两者区别

在只有用户级线程的系统内,CPU调度还是以进程为单位,处于运行状态的进程中的多个线程,由用户程序控制线程的轮换运行;在有内核支持线程的系统内,CPU调度则以线程为单位,由OS的线程调度程序负责线程的调度。

  • 内核支持
    • 用户级线程可在一个不支持线程的OS中实现;
    • 内核支持线程则需要得到OS内核的支持
      亦即内核支持线程是OS内核可感知的,而用户级线程是OS内核不可感知的。
  • 处理器分配
    • 在多处理机环境下,对用户级线程而言主,内核一次只为一个进程分配一个处理器,进程无法享用多处理机带来的好处;
    • 在设置有内核支持线程时,内核可调度一个应用中的多个线程同时在多个处理器上并行运行,提高程序的执行速度和效率。
  • 调度和线程执行时间
    • 设置有内核支持线程的系统,其调度方式和算法与进程的调度十分相似,只不过调度单位是线程;
    • 对只设置了用户级线程的系统,调度的单位仍为进程

3.线程模型

1 : 1(内核线程–HotSpot)

  • 特点
    • 有了内核线程,每个用户线程被映射或绑定到一个内核线程。
    • 用户线程在其生命期内都会绑定到该内核线程。一旦用户线程终止,两个线程都将离开系统。这被称作"一对一"线程映射。(反过来,一个内核线程不一定就会对应一个用户线程)。
    • 一般一直使用API或者是系统调用创建的线程均为一对一线程。例如,linux使用clone创建的线程,以及win下使用CreateThread创建的线程。
  • 弊端
    • 内核线程数量有限
    • 许多操作系统内核线程调用的时候,上下文切换的开销很大。

N : 1(用户线程)

  • 特点
    • 多对一的模型将多个用户线程映射到一个内核线程。
    • 多对一模型线程的切换速度要快很多(线程之间的切换由用户代码来执行)
  • 弊端
    • 如果其中一个线程阻塞,那么所有线程将无法执行。

M : N(混合–golang)

  • 特点
    • 将1:1,N:1 的特点进行综合,即将多个用户线程映射到少数但不只一个内核线程中去。
    • 既存在用户线程,也存在轻量级进程,库和操作系统都可以管理线程。
      • 用户线程还是完全建立在用户空间中,由运行时库调度器管理,因此用户线程的创建、切换、析构等操作依然廉价,并且可以支持大规模的用户线程并发。
      • 内核线程由操作系统调度器管理
      • 操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁,可运行的用户线程由运行时库分派并标记为准备好执行的可用线程。操作系统选择用户线程并将它映射到线程池中的可用内核线程。多个用户线程可以分配给相同的内核线程大大降低了整个进程被完全阻塞的风险。
    • 多对多模型对用户线程的数量没有什么限制,在多处理器系统上也会有一定的性能提升,不过提升的幅度比不上一对一模型。
      在这里插入图片描述
      作为异步回调以外的另一种解决方案,这种m:n的线程模型可以说大有可为,Golang的协程就是使用了这种模型,在用户态,协程能快速的切换,避免了线程调度的CPU开销问题,协程相当于线程的线程

猜你喜欢

转载自blog.csdn.net/weixin_43935927/article/details/108587147