多线程——进程与线程(详解)

news2024/12/28 21:02:47
  • 前言

        在前一篇文章末尾我简单介绍了操作系统,在操作系统中有一个核心的概念就是进程,然而从本篇文章起,就要开始JAVA语言多线程的讲解了,所以在此之前,本篇文章作为多线程的前序铺垫,一是介绍进程与线程的相关概念,二是区分进程与线程,三是解释为什么JAVA语言中不推荐使用多进程的方式进行编程而是更多使用多线程式编程,下面开始今天的知识分享吧!!

一、进程的相关概念

1. 什么是进程

        我们现在所用的系统都是“多任务操作系统”,同一时刻,可以同时运行多个任务,这些正在运行的程序,就可以成为是“任务”,也叫做“进程”,进程包含了一个程序的所有状态信息,但是进程和程序还是有一些区别的,程序是静止的,可以看作是代码+数据,而进程是动态的,可以看作是程序+运行的上下文,进程具有动态性,独立性和并发性(宏观上,各进程同时运行),如下图所示,就是我电脑上正在运行的程序:

        从上图中可以看到,每个任务(进程)在执行的过程中,都需要消耗一定的硬件资源,换而言之,可以认为,计算机中每个进程在运行的时候都需要给他分配一定的系统资源, 这就表明了,在操作系统内部中,进程是操作系统进行资源分配的基本单位

2. 进程控制块

        进程作为操作系统的核心概念,那么操作系统是如何管理进程的呢?其实操作系统在进行管理进程的时候跟学校管理学生或者公司管理员工的基本思路都是一样的,都是先描述,再组织,以学校管理学生为例,学校首先要把一个学生的各种属性表示出来,再使用一定的数据结构把所有的学生信息整理到一起,操作系统也是一样,先把实体属性列出来,由于操作系统一般是用C/C++实现的,所以一般使用的都是结构体来表示进程信息,在操作系统中,把这个用来描述进程实体属性的结构体称为进程控制块(PCB),PCB(Process Control Block)是操作系统学科中的通用概念,其中包含了上百个属性,下面我将选择几个核心属性进行介绍。

①PID进程标识符

        PID是进程的身份标识,这里是通过一个简单的不重复的整数来进行区分的,系统会保证在同一个机器上,同一时刻,每个进程的PID都是唯一的,如下图所示:

         在这里我们选择一个进程,并且点击结束任务,此时,就是任务管理器获取到你当前选中进程的PID然后调用系统的API,把PID作为参数传过去,从而完成这里杀死进程的操作。

②内存指针

        内存指针描述了进程使用内存资源的详细情况,进程运行过程中,需要消耗一些系统资源,其中内存就是一种重要的资源,整个系统中内存有很多,但是这些内存不是可以随意使用的,进程需要先从系统这里申请,然后系统给进程分配一块内存,进程才可以使用,每个进程都必须使用自己申请到的内存,内存指针就是用来描述当前这个进程都能使用哪些内存,一个进程在运行的时候需要有“指令”也需要有“数据”,指令和数据都是加载到内存中的,进程也需要知道自己的内存中哪里存的是指令,哪里存的是数据,这也需要依靠内存指针。

③文件描述符表

        文件描述符表描述了这个进程所涉及的硬盘相关的资源,我们的进程经常要访问硬盘,操作系统对于硬盘这样的设备进行了封装,也就是我们电脑上的文件,在上篇文章介绍了冯诺依曼体系结构,里面的内存器=内存+外存(硬盘,U盘,光盘……),操作系统把外存都进行统一的抽象,都是按照“文件”的方式来进行操作,一个进程想要操作文件,就需要先“打开文件”,就是让进程文件描述符表中分配一个表项(构造一个结构体)表示这个文件的相关信息,打开文件这种操作学过C语言都知道需要使用fopen这样的标准函数,在操作系统中提供的API中(Linux为例)打开文件的函数就叫open。

④进程状态

        进程状态用来描述某个进程是否能够去CPU上执行,有时候某个进程此时不方便到CPU上执行,例如这个进程正在通过Scanner等待用户输入内容,用户什么时候输入是一个完全不可控的事情,这时候就需要用进程状态来告诉CPU此时进程是否可以去执行,如下图所示,是进程的三种基本状态,每个进程在某一时刻只能处于其中一种状态,各个状态的转换关系也如图所示:

        下面简单介绍一下这三种基本状态:

  1. 运行状态(Running):已经在CPU上执行了;
  2. 就绪状态(Ready):随时准备好到CPU上执行,操作系统一打招呼就上了;
  3. 阻塞状态(Blocked):这个进程当前不方便到CPU上执行,不应该调度它(比如,此时进程正在等待IO,来自控制台的输入输出/硬盘的输入输出/网卡的输入输出)。

⑤进程优先级

        在多个进程等待系统调度时,多个进程之间调度的先后关系,调度时间都不是那么平均,这些都是可以通过系统API进行调配的,比如在电脑上运行吃鸡和qq,此时吃鸡的优先级会更高,因为qq信息晚1s收到没什么事,但是游戏延迟1s可是会造成非常糟糕的体验。

⑥记账信息

        记账信息会针对每个进程占据了多少CPU时间进行一个统计,会根据这个统计结果来进一步调整进程调度的策略,确保每个进程都不至于出现完全捞不到CPU的情况。

⑦上下文

        上下文是用来支撑进程调度的重要属性,相当于游戏中的存档与读档,在每个进程运行的过程中会有很多的中间结果,这些中间结果会保存在CPU的寄存器中,上篇文章中以计算3+14为例介绍了指令执行的过程,CPU会先使用寄存器来保存3和14,然后再使用寄存器保存17,假如在寄存器保存完3和14时,这个进程被调度走了,那么在这个进程重新回到CPU上执行时需要将之前在CPU上执行时关键寄存器的数据加载回来,这就需要我们的上下文进行帮助。

        操作系统调度进程的过程可以认为是“随机”的,任何一个进程,代码执行到任何一条指令时,都可能被调度出CPU,此时,就需要在进程调度出CPU之前把当前寄存器中的这些信息给单独保存到一个地方,这也就是存档;在该进程下次再到CPU上执行的时候再把这些寄存器的信息给恢复回来,这也就是读档。

所谓的“保存上下文”就是把CPU的关键寄存器中的数据,保存到内存中(PCB的上下文属性中);

所谓的“恢复上下文”就是把内存中(PCB的上下文属性中)的关键寄存器中的数据,加载到CPU的对应寄存器中。

        进程控制块将进程描述出来后,再将其组织起来用的是一种类似于链表更复杂的链式结构,在我们任务管理器中看到这些进程时,意味着系统内部在遍历链表,并且打印每个节点的相关信息,如果运行一个新的程序,系统中就会多一个进程,多的这个进程就需要构造出一个新的PCB,并添加到链表上,如果某个运行中的程序退出了,就需要把对应进程的PCB从链表中删除掉,并且销毁对应的PCB资源。 

3. 进程调度

        一个进程,消耗CPU资源,这是如何消耗的呢?CPU可以看作是一个舞台,执行指令的就是演员(进程要执行指令),一个CPU可能就一个核心,也可能有多个核心,每个核心都是一个舞台,演员需要登上舞台才能进行表演,并且同一时刻,一个舞台上只能有一个演员,当前我使用的这台电脑上有16个逻辑核心,但是我系统上运行的进程远远不止16个,如下图所示: 

        此时,面对这种狼多肉少的情况该怎么做呢? 这就涉及到一个非常关键的概念,分时复用(并发),例如CPU核心只有一个,先执行进程1代码,执行一会儿后,让进程1下来,进程2上;进程2执行一段时间后,再让进程3上,以此类推,只要切换的速度足够快,人是感知不到这个切换的过程的,所以在我们看来,多个进程就是在“同时执行”的。这里每个进程在某一时刻谁在CPU上执行,执行多久,就是进程调度,针对进程的调度有一系列的调度算法,如:先来先服务算法、短作业优先算法和时间片轮转算法等,这里不对这些算法进行展开介绍了,这里主要介绍进程调度是怎么一回事?,为什么需要进程调度?

        现在我们电脑基本都是多核CPU,同时执行进程也就变得更加复杂了,假如我们电脑的CPU是一个四核CPU,此时就可以同时有四个不同的进程在各自的舞台上进行执行,微观上,这几个进程也是“同时执行的”而不是靠快速切换模拟的“同时执行”这种执行方式也称为“并行执行”,与之对应上面提到的并发执行,仍然存在,每个核心仍然需要分时复用,仍然要快速切换。当前我们的计算机执行的过程往往是并行+并发同时存在的,至于两个进程是并行执行还是并发执行,都是看系统如何进行调度,因此,我们往往就把“并行”和“并发”统称为“并发”,对应的编程方式,也就称为“并发编程”,有的浏览器在下载文件的时候就会应用并发编程的方式来加快IO效率。

4. 内存管理

        进程如何管理内存是一个非常复杂的事情,这里我们只要知道,每个进程的内存是彼此独立互不干扰的,在通常情况下,进程A不能直接访问进程B的内存,这也是为了系统的稳定性,假如某个进程代码出现了bug(比如内存写越界),此时出错的范围只会影响到自己这个进程,不会影响其他进程,这也称为“进程的独立性”。

5. 进程间通信

        虽然有进程独立性,但是有的时候也需要多个进程相互配合,完成某个工作,进程间通信和进程的“独立性”并不冲突,系统提供了一些公共的空间(多个进程都能访问到),让两个进程借助这种公共空间来进行数据交互,操作系统为“进程间通信”提供的具体方式,其中有很多种方式的本质思路都是这种思路,⽬前,主流操作系统提供的进程通信机制有如下:

  1. 管道
  2. 共享内存
  3. 文件
  4. 网络
  5. 信号量
  6. 信号

        其中文件与网络是Java程序员主要使用的进程间通信方式,网络是可以支持同一个主机的不同进程,也能支持不同主机的不同进程,这种方式适用性更高,在后端,很可能就是一组服务器,这组服务器之间进行通信,文件这种方式,其实就是两个进程都对一个文件进行操作,在这个文件中完成数据的交互。 

二、线程的相关概念

1. 线程存在的意义

        本质上来说,引入进程是为了解决“并发编程”这样的问题的,事实上,进程也是可以很好的解决并发编程这样的问题,但是,在一些特定的情况下,进程的表现不尽人意,比如在有些场景下,需要频繁的创建和销毁进程的时候,此时使用多进程编程,系统的开销就会很大。在编写服务器程序使用多进程编程时,由于服务器在同一时刻会收到很多请求,针对每个请求都会创建一个进程,给这个请求提供一定的服务,返回对应的响应,一旦这个请求处理完了,此时这个进程就要销毁了,如果请求很多,就意味着,服务器需要不停的创建新的进程,也不停的销毁旧的进程,这就涉及到了进程频繁创建和释放,这样操作开销大的最关键原因就是资源的申请和释放,进程是资源分配的基本单位,一个进程刚刚启动的时候,首当其冲的就是分配内存资源,进程需要把依赖的代码和数据从磁盘中加载到内存中,然而,从系统分配内存并不是一个容易的事,一般来说,申请内存的时候需要指定一个大小,系统内部就会把大小空闲的内存通过一定的数据结构给组织起来,实际申请的时候就需要去这样的空间中进行查找,找到个大小合适的空闲内存分配过来,总而言之,进程在频繁创建和销毁时开销比较大。

        为了解决上述问题,线程就登场了。

2. 什么是线程

        线程也可以称为“轻量级进程”,它是在进程的基础上做出了改进,线程保持了独立调度执行,这样的“并发支持”,同时省去了“分配资源”,“释放资源”带来的额外开销,那么线程是如何做到这些的呢?在前面介绍了使用PCB(进程控制块)来描述一个进程,在线程这里使用的是TCB(线程控制块)来进行描述一个线程。

        在线程控制块中也有状态、优先级、上下文、记账信息等,其中线程在系统中的调度规则和之前的进程是一样的,在PCB中有个属性是内存指针,在TCB中多个线程的内存指针指向的是同一个空间,这就意味着,只有第一个线程创建的时候需要从系统中分配资源,后续创建的线程就不必再进行分配了,直接共用前面的那份资源就可以了,如下图所示是PCB与TCB内存指针的区别:

        除了内存之外文件描述符表(操作硬盘)这个东西也是多个线程共用一份的。当然,不是随便搞两个线程就能资源共享,把能够资源共享的这些线程分成组,称为“线程组”,线程组是进程的一部分。

三、进程与线程的关系与区别

1. 进程与线程的关系

        通过上述的描述,不难看出线程属于进程的一部分,下面通过画图的方式来进一步展现线程与进程之间的关系,如下图所示:

         通过上图可以看出,进程与线程之间是包含关系,进程是包含线程的,每个进程都可以包含一个或多个线程,由于一个进程中至少包含一个线程,所以也可以说在创建第一个线程的同时,进程也就被创建出来了,在进程创建的时候同时进行了资源的分配,分配的资源包含了所有线程依赖的数据和代码,这些线程各取所需,也可能是有一定公共的,申请的空间不够大可以多次进行申请,在资源分配过程里,不是一锤子买卖。

2. 进程与线程的区别

        介绍完进程和线程是什么,这里总结一下进程与线程的区别,大致有以下几点:

  1. 进程是包含线程的;
  2. 每个线程是一个独立的执行流,可以执行一些代码,并且单独的参与到CPU的调度中(状态,上下文,优先级……这些线程都有自己的一份,在TCB中);
  3. 每个进程有自己的资源,一个进程中的线程共用这一份资源(内存空间和文件描述符表);
  4. 进程和进程之间,不会互相影响,但是如果一个进程中某个线程抛出异常,是可能会影响到其他线程的,可能会把整个进程中所有线程都异常终止;
  5. 同一个进程中的线程之间,可能会互相干扰,引起线程安全问题
  6. 线程不是越多越好,要能够合适,如果线程太多,调度的开销可能会非常明显。

        这里要着重记住:进程是资源分配的基本单位,线程是调度执行的基本单位。 

·结尾

        在我们写代码的时候,可以使用多进程进行并发编程,也可以使用多线程进行并发编程,在Java中,不推荐使用多进程进行并发编程,因为很多和多进程编程相关的API在Java标准库中没有提供,而Java标准库中把系统提供的多线程编程的API都进行了封装,在编写Java代码时就可以使用了,还有一点,多线程在并发编程需要频繁创建销毁的时候效率更高,尤其对于Java进程而言,每个Java进程都是要启动Java的虚拟机的,启动Java虚拟机这样的开销会更大,然而搞多个Java进程就是要启动多个Java虚拟机,这样的开销在多数情况不被接受,所以Java更推荐使用多线程进行并发编程,文章到此就要结束了,本篇文章重点介绍了什么是进程,什么是线程,并介绍了他们之间的关系和联系,如果本篇文章对你有帮助,希望能给博主一个三连鼓励一下吧,您的支持就是我最大的动力,我们下篇文章再见吧┏(^0^)┛~~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2180613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SV830C产品介绍

SV830C产品介绍 SV830C是一款由珠海亿智科技有限公司(Zhuhai Eeasy Technology Co., Ltd.,品牌名为EEASYTECH)倾力打造的专业AI系统级芯片(SoC),专为视频编码产品而设计。这款芯片不仅集成了先进的神经网络…

湖州市自闭症寄宿学校:个性化关爱让每个孩子都能茁壮成长

在探索自闭症儿童教育的广阔领域中,湖州市的自闭症寄宿学校以其个性化的教育模式,为众多家庭点亮了希望之光。然而,当我们把视线转向中国南方的一座现代化大都市——广州,会发现另一所同样在自闭症儿童教育领域深耕细作、成果斐然…

满填充透明背景二维码生成

前几天项目上线的时候发现一个问题:通过Hutool工具包生成的二维码在内容较少时无法填满(Margin 已设置为 0)给定大小的图片。因此导致前端在显示二维码时样式异常。 从图片中我们可以看到,相同大小的图片,留白内容是不一样的。其中上半部分…

RuoYi-Vue实现后台管理系统去掉首页/默认跳转动态路由第一个路由

云风网 云风笔记 云风知识库 RuoYi-Vue 是一个 Java EE 企业级快速开发平台,基于SpringBoot、Spring Security、Jwt、Vue的前后端分离的后台管理系统 内置模块如:部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定…

【代码实现】opencv 高斯模糊和pytorch 高斯模糊

wiki百科 Gaussian Blur,也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像噪声以及降低细节层次。 opencv实现 opencv实现高斯滤波有两种方式, 1、是使用自带的cv2…

怎样把两个视频合并成一个视频?批量合并视频看好这6个工具!

★ 怎样把两个视频合并成一个视频? 随着视频内容的日益丰富,我们常常需要将多个视频片段合并成一个完整的视频文件,不管将2个视频合并拼接到一个进行播放,还是直接合并2个短视频变成长视频,都可以通过这6个工具进行处…

个人项目简单https服务配置

1.SSL简介 SSL证书是一种数字证书,由受信任的证书颁发机构(CA)颁发,用于在互联网通信中建立加密链接。SSL代表“安全套接层”,是用于在互联网上创建加密链接的协议。SSL证书的主要目的是确保数据传输的安全性和隐私性…

PWA(Progressive web APPs,渐进式 Web 应用): manifest.json、 Service Worker

文章目录 引言I 什么是 PWA功能特性技术上分为三个部分安装应用II Web 应用清单将Web 应用清单文件链接到站点manifest.json字段说明III Service Worker( 缓存管理)IV 结合构建工具让项目支持 PWA应用使用插件vite-plugin-pwaworkbox-webpack-plugin插件扩展知识将 PWA 作为脱机…

dwceqos网络驱动性能优化

文章介绍 本文会介绍优化QNX系统下io-pkt-v6-hc驱动模块cpu loading过高问题,经过优化可以降低约一半的cpu loading. 问题背景 激光雷达通过以太网发送数据到ADAS域控中,测试发现在激光功能激活的情况下,会出现比较明显的网络丢帧现象。 …

HT8731 内置自适应H类升压和防破音功能的10W D类及AB类音频功率放大器

1、特点 防削顶失真功能(防破音,Anti-Clipping Function, ACF) 免滤波器数字调制&#xff0c;直接驱动扬声器 输出功率 10W(VBAT4.2V,RL3Ω,THDN10%, fiN 1kHz) 6W(VBAT3.3~4.2V,RL4Ω,THDN<1%,20-20kHz 全频段) 3W (VBAT3.3~4.2V,RL8Ω, THDN<1%, 20- 20kHz 全频段 VB…

DPLL的DCO与PLL的VCO(数控振荡器与压控振荡器)

1.概述 无论DPLL还是PLL都是由PD&#xff0c;LPF和DCO/VCO组成。 PD&#xff1a;鉴相器&#xff0c;是将VCO/DCO输出的频率信号分频后与refclk进行相位比对&#xff0c;输出一个相位差信号 LFP&#xff1a;是将相位差信号转换为VCO的压控信号或DCO的延迟信号。 VCO&#xf…

js将对象的键和值分别归纳进对象,并将多层对象转化成数据的方法

前言&#xff1a; 后端传给我一个没有处理过的json串&#xff0c;但是我要传入el-tree做渲染&#xff0c;此篇来记录一下整个数据处理过程以及el-tree的使用 需求描述&#xff1a; 一、树结构可以展开可以收缩&#xff0c;默认全部展开 二、有一些关键词需要高亮展示红色 …

第十三届蓝桥杯真题Python c组D.数位排序(持续更新)

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;蓝桥杯关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 问题描述 小蓝对一个数的数位之和很感兴趣, 今天他要按照数位之和给数排序。…

Knots_3D 9.3.0 一款教你绑绳结的手机应用

Knots 3D (3D绳结)是一款教你绑绳结的手机应用&#xff0c;可以掌握一些必备的绳结系法&#xff0c;拥有 120 个 3D 效果的绳结&#xff0c;教你系上、解开&#xff0c;户外爱好者必备。Knots 3D已经被全世界的园艺师、渔民、消防员、登山者、军人和童子军使用&#xff0c;它将…

9.2 Linux_I/O_标准I/O相关函数

打开与关闭 文件打开就是判断这个文件资源可不可以被占用&#xff0c;如果可以&#xff0c;就能够打开成功&#xff0c;否则打开失败 文件关闭就是释放文件资源 1、打开文件 1.1 函数声明 FILE *fopen(const char *pathname, const char *mode); 返回值&#xff1a;出错返…

永不失联!遨游双卫星三防手机成为高效应急关键所在

今年9月被戏称为“台风月”&#xff0c;台风“摩羯”、“贝碧嘉”以及热带气旋“普拉桑”接连来袭&#xff0c;极端天气不仅导致了电力中断、道路损毁&#xff0c;更使得传统的通信网络遭受重创&#xff0c;给应急通信保障工作带来了极大的压力。面对“三断”的实战难题&#x…

铰链+屏幕齐发力,告诉你 Mate XT 是如何让折痕变得“无存在感”

“折痕”是折叠手机永恒的话题&#xff0c;每一款折叠屏手机产品的问世&#xff0c;都逃不过对折痕的关注和讨论。 为什么会存在折痕&#xff1f; 材料在弯折的状态下会受到力的作用&#xff0c;在内部产生“压缩”的应力和“拉伸”的应力&#xff0c;这些应力集中在弯折的区…

pytorch线性/非线性回归拟合

一、线性回归 1. 导入依赖库 import numpy as np import matplotlib.pyplot as plt import torch from torch import nn, optim from torch.autograd import Variable numpy&#xff1a;用来构建数据matplotlib.pyplot&#xff1a; 将构建好的数据可视化torch.nn&#xff1a…

《向量数据库指南》——Fivetran 的 Partner SDK:构建自定义连接器和目标

哈哈,说到 Fivetran 的 Partner SDK,这可真是个好东西啊!作为向量数据库领域的“老司机”,我今天就来给大家详细讲讲这个 SDK 的厉害之处,以及如何用它来构建自定义连接器和目标,实现与 Fivetran 自动化数据移动平台的无缝集成。 一、Fivetran Partner SDK:开启自定义连…

二叉树深搜专题篇

目录 计算布尔二叉树的值 求根节点到叶节点数字之和 二叉树剪枝 验证二叉搜索树 二叉搜索树中第K小的元素 二叉树的所有路径 计算布尔二叉树的值 题目 思路 这道题其实是比较简单的&#xff0c;对二叉树来一次后序遍历即可&#xff0c;当遇到叶子结点直接返回叶子节点中…