学习系统编程No.6【进程控制】

引言:

北京时间:2023/3/19/15:16,刚刚睡醒,我发现我真的能睡,早上将反向迭代器剩下的一些知识学完,发现,昨天那篇博客发的有些匆忙了,最后有关反向迭代器的知识都没有把精华部分给分析完整,充分意识到了迭代器的神奇以及泛型编程(模板)在使用上的无敌,模板是真的好用,所以暂时我们先把C++搁置一下,今天我们就来把欠蛋哥的钱还一还,如果再不还,就快要还不起了,今天我们就来学习一下有关进程控制的知识

在这里插入图片描述

复习虚拟地址空间

进程是一个很伟大的概念,从进程出发,从而衍生出了很多关于进程的知识,如虚拟地址空间,虚拟地址空间就是我们的操作系统(OS)给每一个进程画的一个大饼,让每一个进程都可以认为是它在独立的占用和使用整个内存空间,从而方便操作系统进行进程的管理(通过先描述,再组织),从而可以更好的提高操作系统的运行效率,具体管理方式请看上篇博客(mm_struct);并且虚拟地址空间是一个具有很多功能区域的空间,怎么理解虚拟地址空间划分成了很多的区域呢?其实本质理解,虚拟地址空间就是一个线性地址空间,我们通过将该线性地址划分为一个一个的区域(start和end)来控制每一个区域的起始和结束,这样就可以很好的理解,为什么虚拟地址空间有很多的不同区域,并且每个区域有不同的作用(存放的数据信息不同);并且在上篇博客中,我们还了解了有关页表和物理内存的相关知识,浅浅的了解了虚拟地址空间和物理空间之间的关系和之间数据的转换等知识,所以此时我们如果想要更深的了解有关虚拟地址空间、物理内存是如何通过页表进行转换的话,我们需要等我们学习了线程相关的知识,到时候,我们就可以深入理解页表多进程和多线程等知识,此时这里,我们就简单抽象一个操作系统和虚拟地址、物理内存之间的关系

例:在学校中,每一个学生有一个学号和一个宿舍房间号,此时辅导员叫班长去通知某个学号为10的学生去找辅导员(此时的学号就类似于是虚拟地址),此时班长就通过这个虚拟地址在一个班级名单(mm_struct)中去寻找对应的学号,找到之后,查找到了该生的相关信息(具体是那个宿舍房间号),此时班长就通过这个宿舍房间号准确的找到了该学生(在内存中找到了相应的代码和数据),然后通知他,让他去找辅导员,并且当该学生找到辅导员之后(操作系统找到了相应的代码和数据),该学生只会对辅导员说,他是学号为10的学生(而不是某个宿舍房间号),表明操作系统和代码数据之间的交流是通过虚拟地址空间而不是物理内存,并且上述的学号就类似于虚拟地址,而宿舍房间号就类似于物理内存地址;所以上述就是我们抽象出来的操作系统和虚拟地址、物理内存之间的关系,此时有关虚拟地址空间的知识,我们就复习的差不多了,接下来,我们就正式的进入到新知识的学习之中。

进程创建,fork

搞定了上述虚拟地址空间有关的知识,此时我们了解到了,虚拟地址空间概念的提出,主要还是为了服务我们的进程,所以进程才是老大,所以接下来我们就来深入学习一下进程创建等知识,并且在我们目前的理解之中,进程就是等于内核数据结构(pcb)加该进程对应的代码和数据

写时拷贝问题

相信我们以前都见过fork函数,fork函数是linux中一个很重要的函数,它的作用就是用来创建一个新进程,并且运行fork函数,本质上就是在运行一个进程,在执行了该进程之后,fork函数就会执行其对应的代码和数据,本质上是在进行系统调用,此时进行了系统调用之后,内核就会按照fork函数中的代码特性,做如下的工作:

  1. 分配新的内存块和内核数据结构给子进程(虚拟地址)
  2. 将父进程部分数据结构内容(pcb)拷贝给子进程
  3. 添加子进程到系统进程列表当中(pcb队列)
  4. fork返回,开始调度器调度

但是此时注意,当fork函数创建好子进程之后,本质上子进程具有父进程相应的部分代码和数据,并且父进程和子进程指向的代码和数据是在同一块物理内存之上的,只有当子进程或者父进程想要修改它的代码和数据之时,操作系统才会在物理内存上重新开辟一块空间,用于存储子进程或者父进程修改之后的数据(目的:遵守进程具有独立性原则);并且注意,此时父进程和子进程在执行完相应的代码之后,都有一个返回值,父进程返回的是子进程的pid,而子进程返回的是0,和fork创建子进程之后,两个进程分流,谁先执行是由调度器决定;如下图:就是父进程、子进程和物理内存中的代码数据之间的关系(指向的是同一块空间),更深层次的知识,等我们学习了页表的工作原理之后,再展开讲解,此时我们只要明白在不修改之前,它们的代码和数据是物理内存中的同一块空间就行;

在这里插入图片描述
并且此时如果更改了父进程或者子进程其中的代码和数据之后,操作系统就会在内存上开辟一块新的空间供给其存放修改之后的数据,如下图:但是注意,此时只读页表是不会改变(具体详情见页表详解链接)页表相关知识
在这里插入图片描述

所以上述的,操作系统检测到父进程或者子进程有修改数据的行为,然后开辟新空间,并且将我们需要修改的数据拷贝到新空间之中的这个过程,我们就称之为写时拷贝,当我们完成了写实拷贝之后,此时页表会自动的修改相应的和物理内存中代码和数据的映射关系,进而实现进程独立性,此时就又涉及到了页表的相关知识(页表中权限问题),如上所示的只读,只写等!这里不多做讲解,以后肯定是会学习到的,我们只要了解,页表映射关系的修改是有据可循的,不是随意的,感兴趣的同学可以去看上述的那个链接,这里更重要的点是,如何理解为什么要有写时拷贝?

首先我们要明白,操作系统是不允许有任何形式的资源浪费的,所以此时我们就可以明白,只有当父进程或者子进程具有修改数据的意愿之后,我们才进行写时拷贝,而不是把空间提前开好,供给给子进程使用,如果是这样,就会导致物理内存上的两个空间存储了同样的数据(造成浪费),所以写时拷贝概念就是为了解决父进程和子进程不必开辟两块空间,但是又可以修改数据的问题,你修改,我就开辟新空间,你不修改,我就不开辟新空间,按需开辟空间、申请空间

总结: 创建进程需要占用资源(CPU资源、内存资源),原因:因为进程等于内核数据结构加该进程的代码和数据,所以当创建进程,系统多了一个进程之后,操作系统就必须去管理这个进程,当操作系统管理这个进程时,此时管理的方法就和我们日常生活中的管理方法类似(先描述,再组织),所以操作系统就需要为该进程创建对应的内核数据结构对象,并且将该内核数据结构对象给链接到相应的等待队列之中,并且还需要向内存申请空间去存储该进程对应的代码和数据。

注意:我们的fork函数也是存在子进程创建失败的情况的,例如,系统中的进程太多,资源不够用时,实际的进程数超过了限制,所以操作系统是要约束用户创建进程的数量的(防止进程太多把操作系统都搞崩掉)

进程终止问题

首先,进程肯定都是有终止的时候的,并且通过常识可以发现,进程终止是可以分为不同的情况的,一个是进程正常执行完,一个是进程崩溃,并且进程如果正常执行完,此时也可以细分成两类,一个是结果正确,一个是结果不正确,综上,我们在平时写代码、创建进程或者调用某些指令(调用进程)的时候,我们都并不怎么关心一个进程是否正常运行成功,我们关心的往往是该进程为什么结果不正确或者是该进程为什么崩溃,所以,此时我们就一起来探讨一下有关进程终止会出现那些崩溃和结果不正确的情况,如下:
为了了解一下进程的终止情况,此时就会涉及一个叫进程退出码的概念,所以接下来,我们就一起来看看什么是进程退出码;首先在了解进程的退出码这个概念之前,我们要明白,可以通过$?这个符号来获得上一指令的返回值,也就是进程退出码,所以现在问题就变得很简单,想要获得进程退出码,使用$?就行,并且平时我们在写代码的时候,我们经常都要使用main函数,并且使用main函数,我们总是会和return关联使用,从而获得该main函数的进程退出码,如下图代码所示:
在这里插入图片描述
所以此时得出结论:每个进程都有对应的退出码,并且可以使用$?来获取退出码,但是$?只会保留最近一次进程的退出码;

但,我们此时可以发现,当我们获得了退出码之后(0表示成功),剩下的1 2 3 ……等退出码表示的是什么意思呢?所以当我们获取了一个进程退出码之后,我们并不认识,必须要把退出码和相应的描述挂钩,这样我们才可以理解,该退出码表示的意思,进而知道,该进程的退成错误,或者是崩溃原因,如下图就是相应的退出码所对应的错误描述:(通过strerror获取)
在这里插入图片描述
上述代码表示的就是获取0到200进程退出码的对应信息,如下图(在Linux操作系统下一共有134个错误码信息,省略了部分)就是Linux操作系统下了前122个进程退出码信息,例如:此时0退出码表示的就是成功,其余的进程退出码都是相应的错误码,表示着该进程退出的各种原因(注意:我们在Linux中使用的各种指令本质上就是各种的函数接口,本质上就是各种的进程,所以执行一个指令,也就是创建一个进程,并且执行该进程,同理,你也可以获得该进程的进程退出码,成功返回0,非成功返回非1 2 3……);
在这里插入图片描述

总:所以总的来说,一个进程在终止的时候,就有两种可能,一种是异常,一种是执行,异常时,它就会返回相应的异常信号(这点,我们在下述的进程等待中细讲),执行时,分两种,一种是成功,一种是失败,成功返回0,失败返回非0(告诉我们进程为什么执行失败),并且当执行完成之后,进程就退出了,操作系统中就少一个进程,此时操作系统就要释放该进程对应的内核数据结构和独立存储在内存中的代码和数据

常见的进程退出方式

常见的进程退出,main函数return和exit函数code(exit的进程退出码)(main return 0、exit(-1)),但是注意这两个进程退出方式是有很大的差别的,例:普通的函数return仅仅表示的是该函数返回,只有main函数return表示的才是进程退出,且exit(code),可以在代码中的任何位置让该函数表示进程退出而main函数return只能在所有的代码执行完之后才可以退出;了解了这些知识之后,此时我们再来了解一个新的进程退出方式的函数 _exit,这个函数使用上和exit相似,但是功能上差距还是很大的,例:
在这里插入图片描述
使用exit函数,退出时可以获取我们想要打印的内容,使用_exit函数,退出时并不可以获得我们想要的内容,这是为什么呢?原因就涉及到了_exit和exit在功能上的差别和输入输出缓冲区的概念问题了,如下图:

所以总的来说,exit在结束进程的时候,不仅会执行我们定义的清理函数,而且还会将数据从缓冲区中刷新出来,而_exit函数却什么工作都不做,直接就将进程终止,所以使用_exit就看不到我们想要打印的内容,数据任然保存在缓冲区(buff数组)中;并且此时通过上述的现象,此时我们就要明白,我们用户是没有终止进程的权利的,只有操作系统具有终止进程的权利和能力,所以我们在使用exit函数时,本质就是在调用系统接口,让操作系统去完成我们想让它完成的事情(终止进程),所以可以发现,操作系统是exit的顶头上司,但是exit却可以冲刷缓冲区,所以也可以知道缓冲区肯定不在操作系统内部,所以此时引入一个新的概念,缓冲区是在我们的C库中,方便我们接下来了解缓冲区的概念;

所以总的来说exit的工作原理如下:

  1. 执行用户通过 atexit或on_exit定义的清理函数。
  2. 关闭所有打开的流,所有的缓存数据均被写入
  3. 调用_exit(操作系统提供的接口)

总结:进程终止都是通过系统调用来完成的,无论什么方式

什么是进程等待

为什么需要进程等待

想要了解为什么需要进程等待这个概念,此时就涉及到我们之前学习的进程状态问题,当一个进程需要可以让别人去核实该进程退出的原因和结果时,此时这个进程就需要维持僵尸状态,所以僵尸进程的本质就是为了可以让父进程或者操作系统读取到退出结果或者相关信号,但是又由于,僵尸进程已经处于死亡状态,该进程占用的资源不能被操作系统清理,所以此时就有可能导致内存泄露问题,所以为了解决这个问题,此时就需要进行进程等待,因为只有进行了进程等待,目的:获取僵尸进程的退出结果和信息,回收僵尸进程,释放僵尸进程所占的资源;从而将僵尸进程给终止;

进程等待是什么

搞定了上述为什么需要有进程等待问题,此时我们具体的来看一看什么是进程等待,上述我们说了进程等待的目的是为了获取僵尸进程的退出结果或者相关退出信号,所以此时我们可以知道进程等待就是通过系统调用的方式,去获取子进程退出码或者退出信号的一个方式

如何进行进程等待

从上述的进程等待是什么,此时我们又可以了解到,进程等待本质就是去调用系统调用获取进程的退出码或者退出信号,所以此时我们可以知道,进程等待的方式就是去调用系统调用,调用系统调用此时就涉及到了相关的接口函数(操作系统向外提供给用户使用的),我们只有使用该函数,才可以完成系统调用,才可以实现进程等待,所以有关进程等待的接口就是:wait/waitpid,其中wait接口的头文件 #include<sys/types.h>、#include<sys/wait.h>,基本使用方式:pid_t wait(int*status); 如下图:就是一个纯正的进程等待的示例

在这里插入图片描述
循环查看Linux系统中的mytest进程指令:while :; do ps ajx | head -1 && ps ajx | grep mytest | grep -v grep;sleep 1;echo "--------------";done 这样我们就可以一直检测mytest中的代码的执行情况了,如上图中所示,mytest可执行文件中有两个进程在执行,后来一个变成僵尸状态,另一个进程回收僵尸进程等过程,我们通过sleep和进程运行情况,都可以很好的观察出来;

waitpid接口,头文件同上述wait接口一样,基本使用形式:pid_ t waitpid(pid_t pid, int *status, int options);
此时该接口拥有三个参数,pid,status,options
第一个参数pid表示的是等待进程的pid,若pid = -1,那么和wait函数等效,等待的只能是该进程的子进程,若pid > 0,那么就不需要等待该进程的子进程,而是等待任意一个与该pid相等的子进程;

第二个参数status,是一个整形指针类型的参数,并且此时身为指针参数,其可能是一个输出型参数(本质上确实是一个输出型参数),输出型参数指的就是这个参数在函数参数中是未知的,并且这个值是可以修改的,最后可以返回给调用函数,直接改变调用函数中的某个变量的值(等价于,我们可以通过这个输出型参数指针,把这个值拿到该函数外部中被调用函数使用(main函数)),所以本质上waitpid中的这个status输出型参数,就是用来把该进程的退出码和退出信号返回给调用函数(调用进程(bash/shell)(操作系统)),从而让父进程或者操作系统知道你的退出码和退出信号;

第三个参数options,该参数用于若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待;若正常结束,则返回该子进程的pid;

深入理解退出码和信号

承接上述waitpid的第二个参数,status,从该参数我们了解到,设置这个参数的目的是为了可以让外界获取该进程的退出码和退出信号,所以我们接着这个点,来谈谈什么是退出信号,如下图:就是Linux操作系统下的退出信号
在这里插入图片描述
指令:kill -l 发现在Linux操作系统下,一共有64个退出信号(没有0信号),明白了这点之后,我们就可以发现,不仅是我们上述所说的退出码,就连我们的退出信号也都是通过编码的形式来实现的,但是此时就会有一个问题:就是我们的 status 输出型参数,不过只是一个int*类型的指针,它是怎么同时返回退出码和退出型号的呢?不是说退出码和退出型号是两个完全不同的编码吗?怎么可以同时使用一个参数返回呢?所以此时想要弄懂这个问题,就需要引入一个新的概念,位图结构,引入这个概念之后,我们就能了解到,我们不能把int* status纯纯的当作一个完整的整数理解,而是要把它看做是一个位图结构

理解位图结构
上述的 int* status ,根据位图结构来理解,此时我们可以把status理解成是一个具有32个比特位的位图结构(指针类型只是针对于输出型参数),所以此时就可以将32个比特位通过位图的方式分开理解,不同区域的比特位具有不同的含义,如下图所示:
在这里插入图片描述
如上图,按照位图结构,此时我们就可以从一个整形数据中获得两个不同的返回值了,并且此时涉及到一个core dump标志(这里我们不多做理解),并且如下图:

我们可以发现,我们获得的是一个27392的值,并不是我们想象中的107,所以充分表明,这个status表示的不是一个单独的整形数值,而是表示一个位图结构,通过位运算获取数值

并且我们要明白,如果退出信号为0,表示的就是没有收到信号,也就是表示该进程正常执行(不存在异常),此时只要注意它的退出码就行,退出码为0,进程成功执行,非0则表示失败,但是如果,此时退出信号不为0,那么就会导致该进程是异常的,所以此时退出码无论为0还是不为0就没什么太大的影响了,因为,此时的进程已经异常了,退出码表示成功还是不成功该进程都是有问题的,所以我们已经不关心了,只需要关系,返回的退出信号就行。

该博客摆烂,要去上课了,不然还可以写,但是不怕,下篇博客承接,撤退!

在这里插入图片描述

总结:进程不愧是进程,就是牛,欠蛋哥的钱总算是还了一点点了,但是杭哥又来要债了,烦恼!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用旧电脑玩Linux

今天给大家讲讲使用旧电脑玩Linux&#xff0c;大家应该都知道旧电脑的硬件一般比较落后&#xff0c;特别是一些非常老的电脑&#xff0c;目前还在使用的是机械硬盘&#xff0c;如是要跑windows可想而知&#xff0c;但是Linux系统对硬件性能的要求可比windows低的多了&#xff0…

常用React Hooks大合集(二)

React Hooks useRef useRef返回一个ref对象&#xff0c;返回的ref对象再组件的整个生命周期保持不变。 最常用的ref是两种用法&#xff1a; 用法一&#xff1a;引入DOM&#xff08;或者组件&#xff0c;但是需要是class组件&#xff09;元素&#xff1b;用法二&#xff1a;保…

C#中的DataGridView中添加按钮并操作数据

背景&#xff1a;最近在项目中有需求需要在DataGridView中添加“删除”、“修改”按钮&#xff0c;用来对数据的操作以及显示。 在DataGridView中显示需要的按钮 首先在DataGridView中添加需要的列&#xff0c;此列是用来存放按钮的。 然后在代码中“画”按钮。 if (e.Column…

JVM监控搭建

文章目录JVM监控搭建整体架构JolokiaTelegrafInfluxdbGrafanaJVM监控搭建 整体架构 JVM 的各种内存信息&#xff0c;会通过 JMX 接口进行暴露。 Jolokia 组件负责把 JMX 信息翻译成容易读取的 HTTP 请求。Telegraf 组件作为一个通用的监控 agent&#xff0c;和 JVM 进程部署在…

ChatGPT推出第四代GPT-4!不仅能聊天,还可以图片创作!

3月15日凌晨&#xff0c;OpenAI震撼发布了多模态预训练大模型 GPT-4。 根据官网发布的通告可以知道&#xff0c;GPT-4 实现了以下几个方面的飞跃式提升&#xff1a;强大的AI创作识图能力&#xff1b;文字输入限制提升至 2.5 万字&#xff1b;回答准确性显著提高&#xff1b;能够…

【基础算法】数组相关题目

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招算法的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于代码随想录进行的&#xff0c;每个算法代码参考leetcode高赞回答和…

DJ2-4 进程同步(第一节课)

目录 2.4.1 进程同步的基本概念 1. 两种形式的制约关系 2. 临界资源&#xff08;critical resource&#xff09; 3. 生产者-消费者问题 4. 临界区&#xff08;critical section&#xff09; 5. 同步机制应遵循的规则 2.4.2 硬件同步机制 1. 关中断 2. Test-and-Set …

人工智能前沿知识

本来想着初试完学习一下李沐大神的《动手学深度学习》这本书的&#xff0c;但是时间仓促&#xff0c;完全来不及。只能先自行了解一些知识&#xff0c;之后再深入了解。 这里为面试应答&#xff0c;问了chatgpt一些关于AI前沿的知识&#xff1a; 还需要再了解一番&#xff1a;…

网站经常被DDOS攻击的原因有哪些

过去这几年&#xff0c;互联网创业热潮非常火&#xff0c;几乎所有行业都向互联网转型。很多互联网公司都会经历被 DDoS 攻击&#xff0c;导致网站服务瘫痪&#xff0c;用户流失以及公司信用度的负面影响。什么是 DDOS 攻击呢&#xff1f;DDOS 攻击是通过控制大量僵尸网络向服务…

人工智能的几个研究方向

人工智能主要研究内容是&#xff1a;分布式人工智能与多智能主体系统、人工思维模型、知识系统、知识发现与数据挖掘、遗传与演化计算、人工生命、人工智能应用等等。 其中热门研究有以下几种。 一、计算机视觉 就包括图像识别&#xff0c;视频识别&#xff0c;具体应用有人…

Python 基础教程【3】:字符串、列表、元组

本文已收录于专栏&#x1f33b;《Python 基础》文章目录&#x1f315;1、字符串&#x1f95d;1.1 字符串基本操作&#x1f34a;1.1.1 字符串创建&#x1f34a;1.1.2 字符串元素读取&#x1f34a;1.1.3 字符串分片&#x1f34a;1.1.4 连接和重复&#x1f34a;1.1.5 关系运算&…

Java序列化与反序列化

优秀博文&#xff1a;IT-BLOG-CN 序列化&#xff1a;把对象转换为字节序列存储于磁盘或者进行网络传输的过程称为对象的序列化。 反序列化&#xff1a;把磁盘或网络节点上的字节序列恢复到对象的过程称为对象的反序列化。 一、序列化对象 【1】必须实现序列化接口Serializabl…

RK3568平台开发系列讲解(驱动基础篇)I2C协议介绍

🚀返回专栏总目录 文章目录 一、I2C基本读写过程二、通讯的起始和停止信号三、数据有效性四、地址及数据方向五、响应沉淀、分享、成长,让自己和他人都能有所收获!😄 📢I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。 一、…

《网络安全入门到精通》 - 2.1 - Windows基础 - DOS命令Windows防火墙Windows共享文件

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「订阅专栏」&#xff1a;此文章已录入专栏《网络安全入门到精通》 Windows基础一、DOS命令1、目录文件操作dir 列出目录文件cd 切换目录md 创建目录rd 删除目录move 移动文件或目…

4.网络爬虫—Post请求(实战演示)

网络爬虫—Post请求实战演示POST请求GET请求POST请求和GET请求的区别获取二进制数据爬[百度官网](https://www.baidu.com/)logo实战发送post请求百度翻译实战使用session发送请求模拟登录17k小说网常见问题前言&#xff1a; &#x1f4dd;​&#x1f4dd;​此专栏文章是专门针对…

数据结构笔记

文章目录第一章&#xff1a;数据结构与算法第二章&#xff1a;稀疏数组和队列一 、稀疏sparsearray 数组&#xff08;一&#xff09;案例需求&#xff08;二&#xff09;稀疏数组介绍&#xff08;三&#xff09;应用实列&#xff08;四&#xff09;代码实现二、队列&#xff08…

动态规划算法

一、前言动态规划是一种常用的算法&#xff0c;在算法领域十分重要&#xff0c;但对于新手来说&#xff0c;理解起来有一定的挑战性&#xff0c;这篇博客将明确步骤来一步一步讲解动态规划到底该如何理解与运用。二、解析动态规划算法1.特点①把原来的问题分解成了【要点相同】…

【Linux】软件包管理器 yum

什么是软件包和软件包管理器 在 Linux 下需要安装软件时&#xff0c; 最原始的办法就是下载到程序的源代码&#xff0c; 进行编译得到可执行程序。但是这样太麻烦了&#xff0c;所以有些人就把一些常用的软件提前编译好, 做成软件包 ( 就相当于windows上的软件安装程序)放在服…

Spring框架中IOC和DI详解

Spring框架学习一—IOC和DI 来源黑马Spring课程&#xff0c;觉得挺好的 目录 文章目录Spring框架学习一---IOC和DI目录学习目标第一章 Spring概述1、为什么要学习spring&#xff1f;2、Spring概述【了解】【1】Spring是什么【2】Spring发展历程【3】Spring优势【4】Spring体系…

java线程之Thread类的基本用法

Thread类的基本用法1. Thread类的构造方法2. Thread的几个常见属性常见属性线程中断等待一个线程小鱼在上一篇博客详细的讲解了如何创建线程,java使用Thread类来创建多线程,但是对于好多没有相关经验的人来说,比较不容易理解的地方在于操作系统调度的执行过程. 我们通过下面代码…