目录
说在前面
最近正在从零开始学习JAVAEE中的多线程开发板块,所以将自己的学习过程用通俗易懂的语言总结归纳成博客记录下来,我会尽可能涵盖JAVAEE中的重点学习内容(包括八股文),目的是让一个小白通过我的博客也能熟练掌握理解JAVAEE的核心内容。
什么是进程
通俗来说,进程可以理解为计算机正在运行的程序,每一个进程都有一块独立的内存空间
进程中还包含许许多多的东西
- 程序代码
- 数据
- 栈
- 上下文
- 状态
- 系统资源
- ......
可以按ctrl+alt+.打开任务管理器,就可以看到当前计算机中正在运行的进程
应用和后台进程都是操作系统中运行着的进程
操作系统
操作系统的存在是为了负责管理和调度这些进程,使得他们可以正常运行,并且保证进程间不会互相干扰。
引入进程,目的就是为了可以实现多个任务并发执行这样的效果。可以按ctrl+alt+.打开任务管理器,就可以看到当前计算机中正在运行的进程
并发
并发是指同时执行多个任务或操作的能力,在我以前的文章中举过一个餐馆的例子
一个餐馆有许多桌客人就餐,这就是一个并发过程,将餐馆比作操作系统,那么每一个桌子就代表一个进程,在桌子上就餐的客人就是一个线程。桌子需要餐馆来配备相关资源(服务员、厨房、餐具等),多个桌子同时就餐互不干扰,这就是并发的过程。请记住这个案例。线程会在后面详细介绍
进程管理
通常我们描述一个进程,都使用一个结构体/类一个进程有哪些信息表示出来,并且使用一定的数据结构将这些把这些结构体/对象放到一起进行组织
我们通常把描述进程的结构体简称为:PCB(process control block)
注意:进程是操作系统进行资源分配的基本单位
PCB
PCB是一个用来描述进程的结构体,pcb中有很多的属性,这里只描述一些核心的结构
- PID :每个进程的唯一身份标识
- 内存指针:当前这个进程使用的内存是哪一部分
- 文件描述符表:进程每打开一个文件,就会产生一个文件描述符,以此来标识这个被打开的文件,一个进程会打开许多文件,对应一组文件描述符,把这些文件描述符放到一个顺序表这样的结构里,就构成了文件描述符表
- CPU资源相关的属性,这些属性都是辅助进行进程调度的
CPU资源相关的属性
- 进程状态:就绪态:已经准备好的进程,随时可以上cpu执行 / 阻塞态:暂时无法上cpu执行的进程
- 进程的优先级:进程间的调度不一定是公平的,有一定的优先级
- 进程的上下文:描述当前进程执行到哪里的存档记录
- 进程的记账信息:统计每个进程在cpu上执行多久来作为调度的参考依据 操作系统往往使用双向链表来组织pcb:
操作系统往往使用双向链表来组织PCB
- 创建一个进程,就是创建链表的结点
- 销毁一个进程,就是删除链表的结点
- 遍历进程列表,就是遍历链表
进程的内存分配
操作系统给进程分配的内存,都是以”虚拟地址空间"的方式进行分配的,也就是说:每个进程访问的内存地址,都不是真实存在的物理内存的地址
如果每个进程都可以直接访问物理内存的地址,那么会产生这样的问题
例如出现一个进程出现代码bug:越界,那么这个进程就可能会直接影响另一个进程的状态,即a的bug导致b的bug。
所以为了避免这种情况发生,在操作系统中往往使用虚拟空间地址
两个进程访问的内存会被一个页表映射到真实的物理内存上,这样两个进程互不感知,自然也就无法互相影响。
站在这两个进程的角度来看,他们代码中操作的内存地址就是0x00-0xff这一区间,他们访问的内存会被页表直接映射到真实物理地址上,而这两个进程自身是感知不到真实物理内存地址的,这样就规避了风险,如果进程1产生野指针,或越界等bug,此时页表无法翻译这些bug,也就无法修改真正的物理内存,从而不会对别的物理内存进行干扰。 页表的最大目的就是为了方便校验:知道当前的地址是否有效 所以就可以引出进程的性质之一:独立性,这大大提示了操作系统的稳定性
页表的最大目的就是为了方便校验:知道当前的地址是否有效
所以就可以引出进程的性质之一:独立性,这大大提示了操作系统的稳定性
进程间通信
由于进程相互独立无法相互感知,这就导致了进程间无法直接进行通信,无法交互完成数据的操作。
为了解决这个问题:进程间就找到了一个公共区域,两个进程借助这个区域来完成数据交换。这是在隔离性的前提下做出一些妥协的。
注意:在java中其实是不鼓励多进程编程的,但也并非完全没有。
线程
在上文描述的进程,其实是比较“重量的”。它的速度慢,消耗资源高。在对进程进行创建、消耗、调度时,成本都是比较高的,进程之所以重量,是因为它涉及到资源分配,而资源分配是一个非常耗时的操作。比如系统给进程分配一块空闲空间,首先要对自身进行遍历,找到一块大小差不多的空间进行分配,而这个遍历的时间是相对较长的。而且同时会有很多进程向系统申请资源。
线程,相对于进程来说,就是一个相对轻量的东西。如果进程是一块内存空间,那么线程就是这块内存空间里的一个执行流,比如进程是一个工厂,那么线程就是工厂中的生产线,如果要扩大生产,是在原有的工厂中增加生产线,还是另辟一块空间在新建一个工厂,其成本和效率一目了然。 多个线程之间也是并发执行的
还记得上面提到的餐馆的案例吗?如果餐桌是进程,那么在餐桌上吃饭的人就是线程,没有进程就没有线程,线程是包含在进程中的
线程轻量的原因
一个进程中的多个线程之间,共用一份系统资源:内存空间、文件描述符表。 一旦第一个进程创建完毕,后续在进行线程创建,就不必进行系统资源申请了。这样效率提高了很多
线程的特点
一般来说,只要系统给的资源足够多,线程是可以一直创建,没有上限的。但即便如此,线程也不是越多越好,例如一个房间放着一张桌子,桌子上有100只鸡,桌子边有10个位置。
当一个人吃的时候效率很低
而十个人吃的时候,是效率最高的时候
而此时,如果在增加人数,就会发生拥挤,导致没吃上的人因为想吃鸡而去挤掉别的人
而如果此时存在一个暴躁老哥,因为没吃上鸡而愤怒掀桌,就会导致一整个房间都吃不上鸡,发生混乱。
这里的例子中,鸡是系统资源,人是线程。而桌子边的数量是CPU的核心数
说明:
- 线程并非越多越好,因为CPU的核心数量是固定的,线程过多,反而会导致效率降低。
- 一个线程的错误,会导致整个进程的错误,即一个线程的错误可能会把全部线程都带走
线程与进程的区别
- 进程包含线程
- 线程有自己的独立内存空间和文件描述符表,同一个进程中的多个线程,共享一份地址空间和符表
- 进程是操作系统分配资源的基本单位,线程是操作系统调度执行的基本单位。
- 进程之间有独立性,一个进程挂了不会影响别的进程,而一个线程挂了可能会把整个进程带走,影响其他线程的