多线程【初阶】

目录

  • 操作系统
  • 进程(process)
    • 概念
    • 操作系统如何管理进程的
      • 先描述一个进程
      • 再组织若干个进程
    • PCB中的一些属性
    • 进程调度是怎样进行的
    • 内存资源如何分配的
  • 线程
  • 进程与线程的区别和联系

操作系统

1.操作系统是一个“软件”,这是计算机中最重要也最复杂的软件之一。
2.操作系统一个“管理的软件”。
对下,要管理好各种硬件设备
对上,要给各种软件提供稳定的运行环境。
多线程【初阶】_第1张图片

进程(process)

概念

在这里插入图片描述
.exe称为“可执行文件”,这些可执行文件都是静静在硬盘中的,在双击之前,这些文件不会对系统有任何的影响。
但是,一旦双击执行这些.exe文件,操作系统就会把exe给加载到内存中,并且让CPU开始执行这些这些exe内部的一些指令(exe里面就存了很多这个程序对应的二进制指令)
把这些运行起来的可执行文件,称为“进程”。

写的代码,最终目的是要跑起来,最终成为一些进程。对于Java代码来说,最终都是通过Java进程跑起来的(此处说的Java进程就是JVM)。

什么是线程?线程是进程的一个部分,进程包含线程,如果把进程想象成一个工厂,那么线程就是工厂中的生产线。

操作系统如何管理进程的

先描述一个进程

明确出一个进程上面的一些相关属性。
此处的描述其实就是C语言中的“结构体”。操作系统中描述进程的这个结构体称为“PCB”(进程控制块)

再组织若干个进程

使用一些数据结构,把很多描述进程的信息给放到一起,方便进行增删改查。
典型的实现就是使用双向链表把每个进程的PCB给串起来。(操作系统的种类是很多的,内部的实现各有不同,以Linux系统为例)。
所谓的“创建进程”,就是先创建出PCB,然后把PCB加载到双向链表中。
所谓的“销毁进程”,就是找到链表上的PCB,并且从链表上删除。
所谓的“查看任务管理器”,就是遍历链表。

PCB中的一些属性

1.pid(进程id):进程的身份标识
2.内存指针:指明了这个进程要执行的代码/指令在内存的哪里,以及这个进程执行中依赖的数据在哪里。
3.文件描述符表:程序运行过程中,经常要和文件打交道(文件在硬盘上的)。【文件操作:打开文件/读、写文件/关闭文件】
进程每次打开一个文件,就会在文件描述符表上多增加一项。(这个文件描述符表可以视为一个数组,里面的每个元素又是一个结构体,就对应一个文件相关信息)

一个进程只要一启动,不管代码是否写了打开/操作文件的代码,都会默认的打开三个文件(系统自动打开)。
标准输入(Syste.in)
标准输出(System.out)
标准错误(System.err)

综上:要想让一个进程正常工作,就需要给这个进程分配一些系统资源,包括内存、硬盘、CPU

进程调度是怎样进行的

所谓的调度就是时间管理。
上面的属性是一些基本的属性,下面的属性,主要是为了能够实现进程调度。
进程调度:是理解进程管理的重要话题。现在的操作系统,一般都是“多任务操作系统”。(同一时刻,很多进程都在运行,此时,我的系统运行着画图板、idea、QQ等)

并行:微观上,两个CPU核心,同时执行两个任务的代码。
并发:微观上,一个CPU核心,执行一会任务1,执行一会任务2,执行一会任务3…在执行一会任务1,只要切换的足够快,宏观上看来,就好像这么多任务在同时执行一样。
并行和并发,只是微观上有区分,宏观上区分不了,微观上区分都是操作系统自行调度的结果。
正因为在宏观上区分不了并发和并行,所以在写代码的时候也就不具体的区分了,实际上同常使用“并发”指带并行+并发。只是在操作系统进程调度的时候稍作区分,但是其他场景上基本都使用并发作为一个统称来代替的。

1.状态
这个状态描述了当前这个进程接下来应该怎么调度。
就绪状态:随时可以去CPU上执行。
阻塞状态/睡眠状态:暂时不可以去CPU上执行。
2.优先级
先给谁分配时间,后给谁分配时间以及给谁分的时间多,给谁分的少。
3.记账信息
统计了每个进程,分别都执行了多久,分别都执行了哪些指令,分别都排队等了多久。
给进程调度提供调度依据。
4.上下文
就表示了上次进程被调度处CPU的时候,当时程序的执行状态,下次进程上CPU的时候,就可以恢复之前的状态,然后在往下继续执行。

进程被调度出CPU之前,要先把CPU中的所有的存储器中的数据给保存到内存中(PCB的上下文字段中),相当于存档了。
下次进程在被调度上CPU的时候,就可以从刚才的内存中恢复这些数据到寄存器中,相当于读档了。

上下文也可以理解为"存档+读档" :例如玩游戏的时候,要去上课,这个时候存档,回来之后读档,就可以接着上次的继续玩。存档存储的游戏信息就是“上下文”。

进程调度,其实就是操作系统在考虑CPU资源如何给各个进程分配。

内存资源如何分配的

1.操作系统上同时运行着很多个进程,如果某个进程出现了Bug,进程崩溃了,是否会影响到其他进程的?现代操作系统(Windows、Linux、Mac)是不会的,能够做到这一点,就是“进程的独立性”来保证的,就依仗了“虚拟地址空间”。
2.进程之间现在通过虚拟地址空间,已经各自隔离开了,但是在实际工作中,进程之间有的时候还是需要相互交互的。
多线程【初阶】_第2张图片
类似的,两个进程之间,也是隔离开的,也是不能直接交互的,操作系统也提供了类似的“公共空间”,进程A把数据放到公共空间,进程B取走,这种方式成为“进程间通信”

  • 操作系统中,提供的“公共空间”有很多种,并且各有特点,有的存储空间大,有的小,有的速度快,有的速度慢,操作系统中提供了多种这样的进程间通信机制。现在最主要使用的进程间通信方式有文件操作、网络操作。

线程

为什么要有进程,因为系统支持多任务,我们也就需要“并发编程”,通过多进程,是完全可以实现并发编程的,但是有问题。如果需要频繁的创建/销毁进程,成本较高,如果需要频繁的调度进程,成本也比较高。
创建进程就得分配资源(分配内存、分配文件),销毁进程也得释放资源(释放内存、释放文件),对于资源的申请和释放,本身就是一个比较低效的操作。
比如:我现在要去配一个电脑,然后老板列了一个配置单,然后老板得去找这些配置(去仓库里了找),就得去仓库的很多很多货里找到合适的配件。

那么如何解决这个问题?
1.进程池。进程池虽然能解决上述问题,提高效率,同时也有问题,池子里闲置 的进程,不使用的时候也在消耗系统资源,消耗的系统资源太多了。
2.使用线程来实现并发编程。
线程比进程更轻量,每个进程可以执行一个任务,每个线程也能执行一个任务(执行一段代码),也能够并发编程。
创建线程的成本比创建进程要低很多。
销毁线程的成本比销毁进程要低很多。
调度线程的成本比调度进程要低很多。

在Linux上也把线程称为轻量级进程

为什么线程比进程更轻量?
进程重量是重在资源的申请释放,线程是包含在进程中的,一个进程中的多个线程,共用同一份资源(同一份内存+文件)。
只是创建进程中的第一个线程的时候(由于要分配资源),成本相对较高,后序这个进程中在创建其他线程,这个时候成本都会更低一些,不必再分配资源了。

举例:
把进程比作一个工厂,假设此工厂要生产一万部手机。要想提高效率
(1)使用两个工厂(相当于多创建了一个进程)
(2)还是一个工厂,在工厂里多加一条生产线,两个生产线并行进行(相当于创建了一个线程)
那么,增加生产线肯定比增加工厂的成本低。

多加一些线程,是否效率进一步会提高呢?
会,但不一定。如果线程多了,这些线程可能要竞争同一个资源,此时,整体的速度就收到了限制。
比如:生产好的手机要装车拉走,只有一个装车点,第一条生产线生产完成后要进行装车,第二条生产线生产好后要装车时是无法进行装车的,卡车被第一条生产线占用着。

进程与线程的区别和联系

1.进程包含线程。一个进程里可以有一个线程,也可以有多个线程。
2.进程和线程都是为了处理并发编程这样的场景。
但是进程有问题,频繁创建和释放的时候效率低,相比之下,线程更轻量,创建和释放效率更高(为什么更轻量?少了申请释放资源的过程)
3.操作系统创建进程,要给进程分配资源。进程是操作系统分配资源的基本单位。操作系统创建的线程,要在CPU上调度执行,线程是操作系统调度执行的基本单位。(上面说的进程调度,更准确的说,其实是调度的线程。每个进程里有一个线程,可以视为在调度进程,如果进程里有多个线程,严格来说,其实是以线程为单位进行调度。)
4.进程具有独立性,每个进程有各自的虚拟地址空间,一个进程挂了,不会影响到其他进程。同一个进程中的多个线程,共用一个内存空间,一个线程挂了,可能影响到其他线程,甚至导致整个进程崩溃。

你可能感兴趣的:(java-ee)