文章大纲
本文围绕进程与线程两大核心主题展开,帮助读者建立操作系统并发编程的基础认知。全文结构如下:
- 引言:从资源调度视角引入进程与线程的基本概念,明确二者在操作系统中的定位。
- 核心章节一:进程:讲解进程的创建方式、PCB 核心属性(pid、内存指针、文件描述表、状态、优先级、上下文、记账信息)、进程在操作系统中的生命周期状态,以及程序从硬盘加载到 CPU 执行的全过程。
- 核心章节二:线程:说明线程与进程的关系、多任务在 CPU 上的并发与并行执行原理,并通过 Java 代码演示创建线程的三种方法、sleep 异常处理方式以及线程的随机调度机制。
- 总结:回顾进程与线程的关键差异,梳理从进程到线程演进的核心动机,为后续深入学习并发编程打下基础。
什么是进程
进程是资源调度的基本单位
一个程序的运行叫做进程
而进程里怎么识别认识有独特的标识吗
站着操作系统角度理解进程
进程是怎么创建的
通过结构体来表述
PCB是线程的唯一标识
PCB是线程的控制块 当有很多进程时 为了方便 通过数据结构组织起来 方便管理
PCB
pid
进程的标识
内存指针
一组指针 指向里面的内容
文件描述表
进程的运行 很多时候需要和硬盘交互 硬盘由多个文件
进程运行时打开文件 每打开一个文件就会记录
进程的状态
就绪 cpu准备去执行它
堵塞 cpu没有去执行它
运行 cpu正在执行
进程的优先级
进程的先后顺序
有些进程得到的cpu资源比较多
进程的上下文
进程在cpu执行 一会失去cpu执行 过一会接着原来的进度执行
保存上下文 把cpu的值(pcb对应属性)保存到内存
恢复上下文 把内存保存的放在cpu运行
进程的记账记录信息
记录哪个进程运行的时间
哪个进程很久没有吃到cpu资源 就倾斜一点给它
进程在操作系统中的状态
1创建线程
创建pcb 通过结构体来描述属性
2销毁线程
想要销毁线程 通过数据结构找到对应的PCB来删除
3查看线程列表
遍历线程 把属性表示出来
程序在操作系统怎么执行的
一个程序在硬盘文件中有一个可执行文件.exe文件
操作系统通过一系列操作加载到内存中
创建PCB 进程属于就绪状态 等到CPU调度成可执行
cpu再在内存中读取指令
调度完之后 回到就绪状态、
阻塞的时候进程依旧存在,PCB 还在,只是不占用 CPU
进入终止态,等待内核清理 PCB,清理完成进程彻底消失。
由于创建和销毁进程是重量级的工程 需要消耗大量的资源 于是引入线程
线程
线程是资源执行的基本单位
线程的调度是由操作系统决定的 不能人为修改
一个进程包含多个线程
在同一个进程中 不同线程之间的资源是共享 不同进程之间互相独立
多个任务在cpu如何执行
过去的电脑只有一个内核 只能执行一个任务 打开一个软件 想要打开另一个必须关闭另外一个
现在 电脑是多核执行
采用分时复用 把同一份时间 分为若个小份
一个cpu来回切换多个线程叫并发执行
多个cpu在执行多个线程 叫并行执行
但是程序员在计算机内辨别不了 统一叫并发
创建线程的方法
1继承Thread类
public class thread00 extends Thread { //创建线程方法1 继承Thread 需要重写run方法 //线程的入口方法 public void run(){ System.out.println("你好"); } public static void main(String[] args) { thread00 n=new thread00(); //调用线程 n.start(); } }2 //继承Runable 实例化对象 作为参数传递给thread
public class thread00 implements Runnable { public void run(){ System.out.println("不见"); } public static void main(String[] args) { thread00 n=new thread00(); Thread n1=new Thread(n); n1.start(); } }3匿名内部类
public class thread00 { public static void main(String[] args) { Thread n=new Thread(()->{ System.out.println("不见"); }); n.start(); } }sleep异常
public class thread00 extends Thread{ //为什么不能抛异常 因为Thread父类没有关于sleep的处理方式 也不能大于父类异常的处理方式 public void run(){ System.out.println("Bu"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } //为什么这里可以之间抛异常 //因为 main方法内有关于sleep处理的异常 public static void main(String[] args) throws InterruptedException { thread00 n=new thread00(); Thread.sleep(1000); } }异常的处理方式
1记录异常信息作为日志 以便后续处理
2设立报警机制
3进行重试 概率问题
睡眠 等待 sleep
让正在执行的线程 让出CPU指定时间
静态方法
public class thread00 extends Thread{ //为什么不能抛异常 因为Thread父类没有关于sleep的处理方式 也不能大于父类异常的处理方式 public void run(){ System.out.println("Bu"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } //为什么这里可以之间抛异常 //因为 main方法内有关于sleep处理的异常 public static void main(String[] args) throws InterruptedException { thread00 n=new thread00(); Thread.sleep(1000); } }线程的随机调度
调度的顺序是随机的 由操作系统的调控器决定 可设置优先级 但是对于操作系统只是参考
public class thread00 { public static void main(String[] args) throws InterruptedException { Thread n=new Thread(()->{ while (true) { System.out.println("1000"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }); n.start(); while (true) { System.out.println("20000"); Thread.sleep(1000); } } }