《JavaEE初阶》进程与线程
文章目录
前言:
本章主要讲解进程与线程各自的概念与联系.
1. 进程:
1.1 进程是什么
应用程序在执行以后的,操作系统会将执行程序的关键信息加载到内存中去,并且开始运行里面的代码,这就形成了一个进程.
-
应用程序一般指exe文件,它是静态的,一般存储在硬盘中
-
进程是动态的,被加载在内存中
1.2 进程管理
在操作系统中,进程是很多个的,操作系统的任务之一就是管理进程.分为两步:
-
先描述:使用一个 类/结构体 表示出进程的关键信息
-
再管理: 使用数据结构.再将多个 对象/结构体 给整理到一起
PCB(进程控制块) 就是一个结构体.每个PCB里面包含了进程的关键信息.
操作系统将PCB串成双向链表,链接起来,其中PCB中的关键信息分为:(可以去研究操作系统源码)
-
进程的pid码: 也就是进程的身份标识.(是唯一的)
-
内存指针: 操作系统需要加载进程的必要数据,内存指针向操作系统指明进程的内存中,哪些部分是指令,哪些部分是数据.
-
文件描述符表: 表示该进程都打开了那些文件
进程相当于操作系统"资源分配"的基本单位
1.3 进程的调度:
在操作系统中,往往会有多个进程,但是CPU是有限的,为了实现多个进程的稳定执行,操作系统通过并发与并行的方式实现多个进程的稳定运行:
并发式的执行
由于CPU的主频为1.9GHZ,如果让CPU进行轮转运行进程,人的感知是无法感知到了.在CPU上, 通过时间片轮转的方式,进程轮流地在CPU上进行运行,但是由于时间片轮转十分地快,所以宏观上感知不到进程的轮流运行,而是感知到多个进程同时运行.微观上并不是同时运行.
宏观上同时运行,微观上轮流运行
并行式的执行
即CPU上有多个核心,每个核心上只能跑一个进程,在某一时刻两个进程同时执行,就是在两个CPU的核心上执行.
宏观上同时运行,微观上同时运行
并发编程的概念:
在开发中开发满足并发+并行的程序.
进程调度的四大属性:
-
进程的优先级:
进程与进程之间是存在优先级的,操作系统会给线程安排一个优先级顺序,谁的优先级高,CPU就先执行哪个进程.
-
进程的状态:
在线程进行优先级排序的时候,一般会先考虑进程的状态,如果进程A没空,则优先安排进程B与进程C
进程的状态有多种,最典型的为以下两种:
-
就绪状态:即随时准备好可以上CPU跑了.
-
阻塞状态:进程在等待某个任务的完成(例如读写磁盘),完成之后才能上CPU跑,否则无法执行.
-
-
进程的记账信息:
进程在操作系统执行的过程中,操作系统会记录每个进程的历史记录,例如操作系统会记录进程在CPU的运行时间,如果某个进程在CPU的运行时间过少,操作系统会做出适当地调整.
-
进程的上下文:
进程是在CPU上轮转运行的,当一个进程从CPU上被切换下来的时候,进程在CPU寄存器的状态会保存在PCB中,下一次进程回到CPU上时,重新向内存中读取该进程的上下文,恢复到寄存器中.
1.4 进程的虚拟地址空间
在操作系统中,进程的运行需要利用到系统资源->内存空间.
每个进程都需要占用一部分内存,也就有了内存地址,操作系统通过指针去访问线程,但是也就导致了指针指的位置是否合法就需要程序员自身去保证,很容易让指针访问到非法的内存空间,在解引用时就可能进而导致进程的奔溃.
这种行为为操作系统带来了极为不稳定的运行环境.
为了确保操作系统能给进程带来稳定的运行环境,操作系统引入了"进程的虚拟地址"
即: 每个进程都只能访问到自己的地址空间,相互之间不会有影响,哪怕指针指错,操作系统也会及时地报错.
MMU是CPU上的硬件设备,它可以将进程的虚拟地址通过一系列操作转化为电脑的物理地址.
虚拟地址带来的问题:
每个进程都有一个虚拟地址,如果多个虚拟地址空间加起来大于内存了怎么办?
-
虽然系统的线程很多,但是同一个时刻执行的进程并没有那么多.
-
即使同一时刻执行的进程很多,有多个进程在跑的情况下,虚拟地址中并不是所有的虚拟地址都被利用,很可能只的虚拟内存为6M,但是它只使用了1M的大小.
-
极端情况下,就是确实多个进程的虚拟空间的占用已经超过了内存,这种情况属于bug,就需要进行代码优化或者扩容了.
-
优化简单思路: 可以考虑分布式的方式分更多组的服务器,每组服务器存储一部分.
1.5进程间通信
进程引入了隔离性,使得系统更加稳定了,但是也就导致了多个进程之间的通信变得困难.
因此操作系统引入"进程间通信",操作系统为进程间通信提供的方法有很多种,但是本质上都是实现了一个多个进程都能访问到的公共资源,借助公共资源进行通信.
2.线程
在上述进程的描述中,并发编程实现了CPU多核运行.
但是如果需要频繁地创建/销毁进程的情况下,系统就会变得十分低效.
创建进程需要:
-
创建PCB
-
分配系统资源 (低效的原因)
-
将PCB通过操作系统内核插入双向链表
通过实现多线程来解决:
一个进程中有很多个线程,每个线程其实也有自己的PCB,也就是一个进程中可能对应多个PCB,同一个进程的多个线程之间共有一份系统资源,也就意味着新创建的线程不必分配新的系统资源.只需要复用即可.也就是说创建线程就只需要:创建PCB再把PCB加入到内核的链表中.
创建线程的开销比创建进程小,销毁线程的开销也比销毁进程小.
线程的概念:
线程,是包含在线程中的"逻辑执行流"(线程可以单独执行一段代码)
操作系统在进行调度的时候,也是以线程为单位的,操作系统不认线程/进程,只认PCB.
线程是不是越多越好?
使用多线程可以提高效率,但是前提是多核资源是充分的,当线程数的过多导致CPU核心逐渐被吃满的情况下,继续增加线程并不会提高运行速度,反而会增加调度的成本.
同时也会导致多个线程在访问同一个变量时发生冲突,也就是线程安全问题.
经典问题: 线程与进程之间的区别:
-
进程包含线程,线程是进程的一部分
-
每个进程有独立的虚拟地址空间,也有独立的文件描述符表,而同一个进程的线程与线程之间共用一份虚拟地址空间与文件描述符表
-
进程与进程之间是相互独立的,不会互相影响,而线程与线程之间是可能产生影响的,例如一个线程的崩溃可能导致另一个线程的崩溃.
-
线程是操作系统执行调度分配的基本单位