JavaEE知识点总结详细版进程操作系统线程

Posted 王嘻嘻-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaEE知识点总结详细版进程操作系统线程相关的知识,希望对你有一定的参考价值。

目录

1、进程

1)概念

2)理解进程

3)盘点做好进程调度遇到的问题

4)进程间通信(Inter-Process Communication / IPC)

2、操作系统 (Operation Syetem / OS)

1)OS的概念

2)OS的职责

3)并行(parallel) vs 并发(concurrent)

4)用户态(user space)vs 内核态(kernel space)

5)在研究OS实现时,可能面临的一些其他问题

3、内存管理

4. 认识线程(Thread)

1) 线程是什么

2) 为什么OS要引入thread这一个概念。

3)进程(process)和线程(thread)的关系

4)"Java线程” vs "OS 线程(原生线程) "

5)什么情况下会出现线程调度(开始选择一个新的线程分配 CPU)


1、进程

1)概念

用户角度看进程

进程是程序的一次执行过程,一个程序可以进行多次执行(表现为多个进程);甚至可以“同时”执行(多个进程同时存在);进程是程序在“运行阶段”的视角主体。

站在 OS 实现的角度看进程

进程是 OS 进行资源分配的基本单位和最小实体。换言之,同一个进程中的资源是共享的(如果存在比进程更低级的单位,是共享的);不同进程之间的资源是隔离的。

OS的职责是协调者/管理者 ;资源是被 OS 管理的资源(硬件资源、软件资源);分配的实体/单位:分配的时候,只考虑到这一层级;基本/最小:允许有更高层次的单位,但最低到进程。

2)理解进程

动态过程     绑定着一个程序   允许同时存在   OS内部进行资源申请的最小个体
OS要管理进程,进程是OS内部非常核心的一个概念。可以把OS的职责做个简单的划分:
1. OS进行CPU协调的模块 —— 进程管理模块: 主要负责进程的调度工作 —— 主持不同进程在 CPU运行的时间划分。实践中,OS需要负责把进程A从CPU上拿下来,把进程B放到CPU上 via 修改PC寄存器的值。
        CPU之前执行的是属于A进程的指令                              CPU去执行属于B进程的指令
                                         通过修改PC,让CPU去执行属于B的指令
2. OS进行内存协调的模块 —— 内存管理模块: 主持内存空间的分配工作 —— 进程通信的问题
3. 硬盘最为主要的二级存储,是比较重要的一个IO设备 —— OS 进行硬盘上的数据的协调的模块:
OS 一般不直接去管理硬盘上的数据,一般是委托文件系统(Filesystem) 进行抽象管理
把硬盘上的数据抽象成文件(包含文件夹/目录)的概念。
——文件管理模块 .
4. 网络管理模块
5. 其他硬件 —— 以文件驱动的形式进行管理 —— 驱动管理

3)盘点做好进程调度遇到的问题

1. 需要把进程(程序执行的过程)这个抽象的概念,用数据表示处理 —— 面向对象的思想
变成数据,才能被计算机进行处理(才能被OS这个软件进行处理)

class Process / PCB Process Control Block  // 对一个进程的对象化(进程中在管理过程中重要的数据的抽象)
0. pid (全局唯一)
1.关联程序的信息(哪个进程下的哪个程序文件等)
2.关于运行的一些信息
  OS需要管理多个进程,通过数据结构组织起来管理List. Map. Queue
  1.哪个用户启动的
  2.进程工作目录(process work directory pwd)
  3.什么时候开始的、什么时候结束的
3.分配的资源
  CPU: CPU的占用率:之前过去的一段时间里,分配给该进程的时间占比
  内存:分配出去的内存(内存不一定连续)
  ...
4.调度时用到的信息...

2. 需要对进程做个区分: 哪些现在可以分配CPU了,哪些暂时没有准备好
通过对进程做状态划分,来区分出处于不同情况下的进程 —— 进程状态 /状态的转义(随着时间变化,进程的情况也在变化)
新建(进程处于正在创建中)、就绪(万物具备,只欠CPU) 、运行(进程的指令其实在CPU运行着)、
阻塞(进程由于等待外部条件,所以暂时无法继续)、结束(进程的所有指令执行结束,但PCB暂时保留,OS还需要做一些其他工作的时候)

 ->新建: 随着程序的启动运行
新建->就绪: 进程的初始化工作完全完成 (这个工作是由OS的指令完成的)
就绪->运行: 进程被OS选中,并分配了CPU之后
运行->结束: 进程的最后一条指令执行结束 (粗略地理解,就是main方法执行结束了)
运行->就绪: 1. 被高优先级的进程抢占了  2. 时间片耗尽  3. 进程可以执行一些 OS 提供的系统调用,主动放弃
运行->阻塞: 等待一些外部条件,如等待IO设备;进程休眠一段时间;;...
阻塞->就绪: 外部条件满足: IO数据来了;休眠时间到了..
结束->: 进程PCB彻底被OS回收了

3. 现在手上有等待分配CPU的所有进程列表一就绪队列,下一个问题,选择哪个进程上CPU?
要求: 要有消息、要保证公平性、要让更紧急的任务被更紧急的处理、低成本解决...
先来先服务;优先级划分(进程PCB中需要管理一个优先级的属性);短作业优先级
4. OS什么时候会介入进程调度:需要选择一个新的进程,进行CPU分配
    1.一个新的进程刚处于就绪状态时,当该进程的优先级较高时 —— 具备这种能力的 OS被称为          抢占式(实时)                                                                                                                                             CPU空闲”下来的时候👇
    2. 运行状态的进程 -> 结束。一个进程结束了
    3. OS每隔一段时间,会调度一次:进程的时间片耗尽
    4. 进程主动放弃CPU :运行 -> 阻塞 ; 运行 -> 就绪
5. OS具体怎么进程切换:通过上下文切换 —— 保护上一个进程的上下文 + 恢复下一个进程的上下文
上下文(Context不是Content)    Context Switch CS   可以用环境这个词去理解
讨论到进程调度中,上下文指的是,以PC寄存器所代表的一组寄存器中的值。
保护上下文:把寄存器中的值,保存到内存的某个位置。
恢复上下文:把内存中之前保护的值,写入寄存器中

4)进程间通信(Inter-Process Communication / IPC)

理论上,进程之间是独立的,但实际中,往往是多个进程之间互相配合,来完成复杂的工作。例如:我们之前使用mysql的场景。通过workbench进程和mysql 服务器进程进行通信,来实现数据的增删查改。所以,有了进程之间交换数据的必要性了。
当下的问题: OS进行资源分配是以进程为基本单位进行分配的,包括内存。分配给A进程的内存
不会分配给B进程。所以,进程A、B之间直接通过内存来进行数据交换的可能性完全不存在了。
[A被隔离][B被隔离]
所以OS需要提供一套机制,用于让A. B进程之间进行必要的数据交换一进程间通信。
进程间通信的常见方式:
1.管道(pipe)
2.消息队列(message queue)
3.信号量(semaphore)
4.信号(signal) 
5.共享内存(shared memory)
6.网络(network)
—— workbench和mysql通信的方式

2、操作系统 (Operation Syetem / OS)

有进程管理,我们引出的一些 OS 概念

1)OS的概念

操作系统支持多用户,多任务场景,OS的职责就是协调专门的软件工作。

硬件(资源)会被多个任务“同时”访问;OS协调不同任务对硬件资源的访问;OS管理着硬件资源,分配给不同的任务去使用。

2)OS的职责

  • OS是一个管理(硬件/软件)资源的软件
  • OS是一个协调者(管理的含义就是做协调)
  • OS是一个分配者,分配的主体就是任务。站在任务的角度,就是一切硬件资源都需要找OS申请后才能被授权使用。
  • OS原来,主要研究的就是“管理学”,如何高效、公平、低耗、稳定的进行资源的分配、协调以及在其中遇到的种种问题。

3)并行(parallel) vs 并发(concurrent)

并行:进程真的在同时在执行(微观角度的同一时刻,是由多个指令在执行的,所以只会在多CPU多核场景下)

并发:进程假的同时在执行(微观上,表现为一次只执行一个进程,但宏观上,多个进程在“同时”执行)

4)用户态(user space)vs 内核态(kernel space)

 

CPU正在执行的是OS的指令时,就进入到内核态。反之,正在执行的是普通进程的指令时,就在
用户态。
管理的核心就是权限的划分
内核态的指令权限高(所有的硬件都能访问);用户态的指令权限低(只能访问OS规定的资源)。
CPU本身就有权限开关,所以可以做到!
内核态指令可以访问所有内存;用户态指令只能访问进程自己的内存。
结论:
内核态的性能较差;用户态的性能较好

5)在研究OS实现时,可能面临的一些其他问题

1.在分配资源时,如何避免出现死锁(dead lock)的问题

某个哲学主要的时间是在思考哲学问题,思考的间隙时不时的要吃饭。吃饭需要筷子(资源)。
哲学家会去申请筷子(资源),哲学家申请到两个资源之后(左边的筷子+右边的筷子),吃饭,释放资源。
如果这个过程没有被精心设计过,可能被遇到以下极端情况:
A get 1 success    A try  5 ,  5已经被分配,则A等待
B get 2 success   同理..
C get 3 success   同理..
D get 4 success   同理..
E get 5 success   同理.. 

解决方法:银行家算法(打破死锁的几个充分条件)
OS这里说的死锁和多线程时说的死锁,有内在的概念上的关联性,但严格上说不是一回事。

3、内存管理

执行流(execution flow):拥有独立 pc 的一套指令;不同的执行流从现象上看起来完全独立。

内存管理:空间上划分(空间划分不保证是连续的)
[内核使用的内存]  [分配给普通进程使用的内存]  [空闲空间]
                                       [进程A][进程B]....
0. Java应用程序员眼中的内存
JVM的内存空间分为栈区、堆区、方法区 (属于Java应用和JVM之间的概念)

1. 线性地址(虚拟地址) vs 物理地址
物理地址: 真实的内存中的地址 ;线性地址: 物理地址被OS进行转换后的一个地址

没有线性地址之前的程序世界:
同一个程序的多次运行,会生成不同的进程
是否能保证同一个进程一定被放到内存的同一个位置?
如果程序员眼中可以直接看到物理地址,意味着程序员必须关心一个问题 —— 不同进程如果出现在内存的不同位置,在程序中处理地址时必须考虑到地址不同带来的复杂性。
所以,引入线性地址这个概念之后,程序员不需要再考虑这个复杂性了。 MMU来管理复杂性

4. 认识线程(Thread

1) 线程是什么

一个线程就是一个 " 执行流 ", 每个线程之间都可以按照顺讯执行自己的代码 多个线程之间 "同时" 执行着多份代码。线程是OS进行调度(分配CPU)的基本单位;线程变成了独立执行流的承载概念,进程退化成只是资源(不含CPU)的承载概念。
还是回到我们之前的银行的例子中。之前我们主要描述的是个人业务,即一个人完全处理自己的 业务。我们进一步设想如下场景: 一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。如果只有张三一个会计就会忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一家公司的业务。 此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别 排队执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread )。

2) 为什么OS要引入thread这一个概念。

由于进程这一个概念天生就是资源隔离的,所以进程之间进行数据通信注定是一个高成本的工作。
现实中,一个任务需要多个执行流一起配合完成,是非常常见的。
所以,需要一种方便数据通信的执行流概念出来,线程就承担了这一职责。 首先," 并发编程 " 成为 " 刚需 "。单核 CPU 的发展遇到了瓶颈 . 要想提高算力 , 就需要多核 CPU, 而并发编程能更充分利用多核 CPU 资源。有些任务场景需要 " 等待 IO", 为了让等待 IO 的时间能够去做一些其他的工作 也需要用到并发编程。 其次 , 虽然多进程也能实现并发编程 , 但是线程比进程更轻量。 创建线程比创建进程更快; 销毁线程比销毁进程更快;调度线程比调度进程更快。

3)进程(process)和线程(thread)的关系

进程 —— 线程是1:m的关系
一个线程一定属于一个进程; 一个进程下可以允许有多个线程。
一个进程内至少有一个线程, 通常被这个一开始就存在的线程,称为主线程(main thread).
主线程和其他线程之间地位是完全相等的,没有任何特殊性。

4)"Java线程” vs "OS 线程(原生线程) "

不同JM有不同的实现,它们的外在表现基本一致,除了极个别的几个现象。Java 线程,一个线程异常关闭,不会连坐。
我们使用的 HotSpot 实现(JVM) 采用,使用一个OS线程来实现一个Java线程。
Java中由于有JVM的存在,所以使得Java中做多进程级别的开发基本很少。Java 中的线程还克服了很多OS线程的缺点。所以,在Java开发中,我们使用多线程模型来进行开发,很少使用多进程模型。
1. Java线程在代码中是如何体现的 —— java.lang.Thread类(包括其子类)的一个对象  Thread——线程
2.如何在代码中创建线程(最基本)  Runnable一让这个线程去完成的工作 (任务)
    1. 通过继承Thread类,并且重写run方法。
        实例化该类的对象-> Thread对象。
    2. 通过实现Runhable接口,并且重写run方法。
       实例化Runnable对象。利用Runnable对象去构建一个Thread对象。

3.启动线程:当手中有一个Thread对象时,调用其start()方法
注意1: 一个已经调用过 start(),不能再调用start()了,再调用就会有异常发生
注意2: 千万不要调用成 run()
进程线程源代码  

5)什么情况下会出现线程调度(开始选择一个新的线程分配 CPU)

  1. CPU空闲
    1.当前运行着的CPU执行结束了         运行->结束
    2.当前运行着的CPU等待外部条件     运行->阻塞
    3.当前运行着的CPU主动放弃            运行->就绪
  2. 被调度器主动调度
    1.高优先级线程抢占
    2.时间片耗尽(这个情况最常见)

小结:

在多线程中,明明代码是固定的,但会出现现象是随机的可能性,主要原因就是调度的随机性体现在线程的运行过程中!!
我们写的无论是Thread的子类还是Runnable的实现类,只是给线程启动的"程序”。所以,同一个程序,可以启动多个线程。


本小节完^_^

以上是关于JavaEE知识点总结详细版进程操作系统线程的主要内容,如果未能解决你的问题,请参考以下文章

多线程重点知识总结

JavaEE之多线程01

Java 多线程与并发:前置知识

成都校区javaEE线程面试总结

[Java] 多线程基础详细总结,附加详细实例

知识储备—01-进程,线程,多线程相关总结