Java多线程与并发学习之(一):进程与线程

时间:2021-04-14 21:20:30

  最近团队内部组织了分享,自己选择的模块是“多线程与并发”,趁着这个机会,自己也好好系统的再学习一遍“多线程与并发”相关的知识。之前自己也学习过该模块内容,但是由于有时候走马观花,加之实际工作中相关经验较少,很多东西一知半解,没有理解透,这次就将相关内容都梳理出来,尽可能弄清楚一点,也为后续复习提供方便。

  谈到线程,一般都会提到另一个概念—进程。那么什么是进程?什么是线程呢?

  这些概念都比较抽象,咱们先来一个最直观的感受,打开Windows系统下的任务管理器,最左侧的一列“映像名称”下都是进程,列“线程数”下对应的是每个进程下对应的线程数。比如咱们启动QQ后,任务管理器里面就会产生一个QQ.exe的进程。

    Java多线程与并发学习之(一):进程与线程

 

  “线程”和“进程”都是操作系统中的概念,最初先有的“进程”的概念,后续产生的“进程”的概念。

  先来放出一个百度给出的“进程”和“线程”的较为官方的定义:


  进程(process):是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。

  线程(thread):有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。


  详细介绍“进程”和“线程”前,先来了解下“任务调度”的概念。大部分操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式,也就是说一个任务执行一小段时间后强制暂停去执行下一个任务,每个任务轮流执行。任务执行的一小段时间叫做时间片,任务正在执行时的状态叫运行状态,任务执行一段时间后强制暂停去执行下一个任务,被暂停的任务就处于就绪状态等待下一个属于它的时间片的到来。这样每个任务都能得到执行,由于CPU的执行效率非常高,时间片非常短,在各个任务之间快速地切换,给人的感觉就是多个任务在“同时进行”,这也就是我们所说的并行执行。CPU在不同任务间切换时,需要消耗一定的资源,比如从执行任务A到执行任务B过程为,先保存任务A当前执行的各种信息,也叫上下文,然后暂定任务A,然后将时间片分配给任务B,读取任务B的上下文,执行任务B。


 进程

  在早期面向进程设计的计算机结构中,进程是程序的基本执行实体,操作系统进行调度的任务就是进程;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。


  狭义定义:进程是正在运行的程序的实例(an instance of acomputer program that is being executed)。

  广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。


  进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。   

  进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。

  操作系统引入进程的概念的原因:

  (1).从理论角度看,是对正在运行的程序过程的抽象;

  (2).从实现角度看,是一种数据结构,目的在于清晰地刻画动态系统的内在规律,有效管理和调度进入计算机系统主存储器运行的程序。


  操作系统的作用是执行各种程序以完成各种特定任务,进程相当于是程序的一次实际执行过程,程序的最终执行依附于进程,因为操作系统是根据进程为维度来进行分配资源的。

  进程的特征

  (1).动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的;

  (2).并发性:任何进程都可以同其他进程一起并发执行;

  (3).独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位;

  (4).异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进;

  (5).结构特征:进程由程序、数据和进程控制块三部分组成。多个不同的进程可以包含相同的程序:一个程序在不同的数据集里就构成不同的进程,能得到不同的结果;但是执行过程中,程序不能发生改变。

 

  线程

  一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

  线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。

  由于执行不同程序时进行进程切换的代价太高,引入了“线程”的概念,被封装到了进程中,也就是“轻量级进程”,将线程作为调度单位。在多线程OS中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。

 

关于“进程”和“线程”的由来,从网络上找到一个自己觉得比较容易理解的解释:

 

  1.在单核计算机里,有一个资源是无法被多个程序并行使用的:cpu。没有操作系统的情况下,一个程序一直独占着全都cpu。如果要有两个任务来共享同一个CPU,程序员就需要仔细地为程序安排好运行计划--某时刻cpu和由程序A来独享,下一时刻cpu由程序B来独享而这种安排计划后来成为OS的核心组件,被单独名命为“scheduler”,即“调度器”,它关心的只是怎样把单个cpu的运行拆分成一段一段的“运行片”,轮流分给不同的程序去使用,而在宏观上,因为分配切换的速度极快,就制造出多程序并行在一个cpu上的假象。

  2.在单核计算机里,有一个资源可以被多个程序共用,然而会引出麻烦:内存。

  在一个只有调度器,没有内存管理组件的操作系统上,程序员需要手工为每个程序安排运行的空间 --程序A使用物理地址0x00-0xff,程序B使用物理地址0x100-0x1ff,等等。然而这样做有个很大的问题:每个程序都要协调商量好怎样使用同一个内存上的不同空间,软件系统和硬件系统千差万别,使这种定制的方案没有可行性。

为了解决这个麻烦,计算机系统引入了“虚拟地址”的概念,从三方面入手来做:

  (1).硬件上,CPU增加了一个专门的模块叫MMU,负责转换虚拟地址和物理地址。

  (2).操作系统上,操作系统增加了另一个核心组件:memory management,即内存管理模块,它管理物理内存、虚拟内存相关的一系列事务。

  (3).应用程序上,发明了一个叫做【进程】的模型,(注意)每个进程都用【完全一样的】虚拟地址空间,然而经由操作系统和硬件MMU协作,映射到不同的物理地址空间上。不同的【进程】,都有各自独立的物理内存空间,不用一些特殊手段,是无法访问别的进程的物理内存的。

  3.现在,不同的应用程序,可以不关心底层的物理内存分配,也不关心CPU的协调共享了。然而还有一个问题存在:有一些程序,想要共享CPU,【并且还要共享同样的物理内存】,这时候,一个叫【线程】的模型就出现了,它们被包裹在进程里面,在调度器的管理下共享CPU,拥有同样的虚拟地址空间,同时也共享同一个物理地址空间,然而,它们无法越过包裹自己的进程,去访问别一个进程的物理地址空间。

  4.进程之间怎样共享同一个物理地址空间呢?不同的系统方法各异,符合posix规范的操作系统都提供了一个接口,叫mmap,可以把一个物理地址空间映射到不同的进程中,由不同的进程来共享。

  5.PS:在有的操作系统里,进程不是调度单位(即不能被调度器使用),线程是最基本的调度单位,调度器只调度线程,不调度进程,比如VxWorks。

 

 进程和线程的区别

  (1). 一个程序至少有一个进程,一个进程至少有一个线程。

  (2).线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位。

  (3).一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线。

  (4). 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见。

  (5). 线程在执行过程中与进程的区别:每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

  (6). 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。

  (7).线程比进程轻量,对线程的调度所付出的开销就会小得多,线程上下文切换比进程上下文切换要快得多,线程能更高效的提高系统内多个程序间并发执行的程度。