线程就是我们所谓的"跑起来的程序",那什么是可以跑起来的程序呢?就是类似我们在桌面上创建的对应软件的快捷方式,右击软件点击打开文件所在位置我们就可以看到xxx.exe文件,这样exe的文件我们统称为可执行文件
当我们不想启动软件时,这些文件就会静静的"呆在"硬盘上,在我们双击之前是不会对系统有任何影响.
但是,当我们一旦双击这些exe文件后,操作系统就会把这些exe的文件加载到内存中,并且让CPU开始执行exe内部的一些指令(exe 里面存放了很多程序点对应的二进制指令).这个时候这些exe文件就不再’"呆呆"的在硬盘上了,把这些文件运行起来的可执行文件,称为**“进程”**.
进程控制块(PCB):
我们知道操作系统里面主要是由C++实现的,此时对线程的描述就类似于用C语言的"结构体"(也就和Java类差不多),操作系统所描述的这个结构体就是统称为"PCB"(process control block)–进程控制块.我们在电脑的任务管理器中可以看到pid - 进程的"身份证号码"
内存指针:
就是当我们想要知道进程要执行的代码/指令在内存的哪里,以及这个进程中依赖的数据在哪里就会用到内存指针
文件描述符表:
可以视为数组,里面的每一个元素都是一个结构体,也就是对应一个文件的相关信息
在程序运行中,经常要和我们硬盘里的文件"打交道",每当进程打开一个文件,就会在文件描述符表多增加一项"元素".
当一个进程只要一启动,不管代码是否写了打开/操作文件的代码,都会系统默认的打开三个文件:标准输入(System.in),标准输出(System.out),标准错误(System.err).
进程的调度分为四个部分:状态,优先级,记账信息,上下文
我来给大家举一个例子:有一位女同学长相十分美丽,身材也好,自然就会有很多男生追求,这个时候这个女生就在想这么多男生追求我,我应该选择那个最我的男朋友比较好呢,她想那我就先选三种类型的男生来同时和我谈恋爱,A男生有钱,B男生有颜,C男生对她好"舔狗".
进程的状态: 进程随时可以去CPU执行就叫做就绪状态,进程暂时不可以去CPU执行就叫做阻塞状态
例子: 当这个女生想要找A去逛街时,A说我在国外办一些事情没有空去陪这个女生,这就是阻塞状态,当女生找C去逛街,c立马答应配女生逛街就是就绪状态
进程的优先级: 就时将CPU执行的顺序优先给到一些线程
例子: 因为B颜值很高,所以女生将自己的周一到周五的时间来和B在一起度过,周六和A在一起度过,周日又可能和C在一起度过.
进程的记账信息: 统计了每个线程都分别执行了多久,分别都执行了那些指令,分别排队等了多久
例子: 因为女生长期不和C在一起,C对女生的好感度降低,对女生舔的力度不够了,女生就会适当的去陪陪C,来增加C对自己的好感度.
进程的上下文: 表示在上一次进程别调度出的时候,程序的执行状态,这样在下一此进程调到CPU时就能接着上次执行的状态继续运行,所以在进程调度之前,都需要先把CPU上的寄存器的数据都保留到内存中,相当于"存档",到下次CPU调用该进程的时候,根据上次在内存存储的数据在进行继续执行,就叫做读档 .
例子: 当女生忘了改A,B,C三个男生的备注了,有一天男生们换头像或者昵称了,女生把A的名字叫成了B的名字,将C的名字也叫成了B的名字这样就会引起A,C的猜疑,所以女生过后就会将A,B,C的备注改成他们的名字
操作系统上同时运行着多个进程,我们来考虑一种情况,就是当一个进程出现bug导致进程崩溃,其他进程会不会受影响呢?对于现代的操作系统,是不会影响的。因为线程之间通过虚拟地址空间,来保证进程之间的独立性。
每个进程只能访问自己的那一块虚拟地址空间的数据,无法访问其他进程虚拟地址空间的数据,这样当一个进程崩溃时,另外的进程不会受到影响,这样就将各个进程隔离起来了。
但是,在实际工作开发中,进程间常常需要进行交互,为了实现进程间的通信,操作系统提供了一块“公共空间”,进程A可以先把数据存入“公共空间”,然后进程B可以到“公共空间”取出数据,这样就实现了进程间的通信。
操作系统提供的“公共空间”有很多种,有储存大的也有小的,有访问速度快的也有慢的,现在最常见的进程间通信机制有:
1.文件操作
2.网络操作(socket)
那么为什么要引出进程这样一个概念呢?因为现代的操作系统都是多任务操作系统,为了最大效率地运行程序,我们需要进行并发编程,毕竟一群人完成任务的速度比一个人要快的多。
我们发现如果需要频繁的创建/销毁进程,这个事情成本是很高的,如果需要频繁的调度进程,这个事情成本也是很高的,那我们应该如何解决呢?
方案A: 使用进程池,与字符串常量池类似,将已经创建的进程存入常量池,后面需要使用时直接加载即可,但是也存在问题,那就是闲置的进程也会占用资源,相当于空间换时间,消耗空间来提升效率。
方案B : 使用多线程来实现并发编程,因为进程包含线程,一个进程中包含多个线程,所以线程比进程轻量,因此Linux中也将线程称为轻量级进程(LWP),正是因为线程比进程轻量的多,线程的创建与销毁的成本很低,因此使用多线程实现并发编程比多进程更加合适。
**例子:**假如华为手机要生产1万台手机,但是一条生产线只可以生产5000台手机
方法1.我们可以分两个工厂来分别生产5000台手机(两个进程)
方法2.我们可以让改工厂在生产出一条可以生产5000台手机的生产线(一个进程,两条线程)
我们可以看出方法1是实现并发编程,方法2实现了多线程并发编程
如上述,知道了进程就是运行的程序,那线程又是什么?线程是进程内部的一部分,如果说进程是一个工厂,那么线程就是工厂中的生产线,所以一个进程可以包括很多个线程,一个进程至少拥有一个线程
1.线程可以包含线程,一个进程里可以有一个线程,也可以有多个线程
2.进程具有独立性,每个进程有各自的虚拟地址空间,一个进程挂了不会影响其他进程,但是一个线程挂了,就会影响其他线程,甚至导致整个进程崩溃
3.操作系统创建进程,要给进程分配资源,进程是操作系统分配资源的基本单位 操作系统创建线程,要在CPU上调度执行,线程是操作系统调度执行的基本单位
4.进程和线程都是为了处理并发编程这个场景,但是在频繁和释放时进程效率低,线程效率高