Free Will

Java学习笔记(11):进程与线程

操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。

有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。

由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。

一、进程

1.1 定义

狭义定义:进程是计算机中正在运行的程序的实例(an instance of a computer program that is being executed)。

程序本身只是指令的集合,进程才是程序(那些指令)的真正运行。用户下达运行程序的命令后,就会产生进程。同一程序可产生多个进程(一对多关系),以允许同时有多位用户运行同一程序,却不会相互冲突。进程需要一些资源才能完成工作,比如CPU使用时间、存储器、文件以及I/O设备,且为依序逐一进行,也就是任何时间内仅能运行一项进程。

1.2 基本状态

通常进程有如下5种状态,其中前三种是进程的基本状态。

  • 1)运行状态(执行窗台):进程正在处理器上运行。在单处理器环境下,每一时刻最多只有一个进程处于运行状态。
  • 2)就绪状态:进程已处于准备运行的状态,即进程获得了除处理器之外的一切所需资源,一旦得到处理器即可运行。
  • 3)阻塞状态:又称为等待状态,进程正在等待某一事件而暂停运行,如等待某资源为可用(不包括处理器)或等待输入/输出完成。即使处理器空闲,该进程也不能运行。
  • 4)创建状态:进程正在被创建,尚未转到就绪状态。
  • 5)结束状态:进程正从系统中消失。可能是进程正常结束或其他原因中断退出运行。

5d3ba5dc75d3423319.jpg
进程的三个基本状态之间只可以相互转换的,如图所示。具体的说:

  • 当一个就绪状态获得处理机时,其状态由就绪变为执行;
  • 当一个运行进程被剥夺处理机时,如用完系统分给它的时间片、出现更高优先级别的其他进程,其状态由运行变为就绪;
  • 当一个运行进程因某事件受阻时,如所申请资源被占用、启动I/O传输未完成,其状态由执行变为阻塞;
  • 当所等待事件发生时,如得到申请资源、I/O传输完成,其状态由阻塞变为就绪

1.3 进程与程序的区别

  • 进程是程序及其数据在计算机上的一次运行活动,是一个动态的概念。进程的运行实体是程序、离开程序的进程没有存在的意义。从静态角度看,进程是由程序、数据和进程控制块(PCB)三部分组成的。而程序时一组有序的指令集合,是一种静态的概念。
  • 进程是程序的一次执行过程,它是动态地创建和消亡的,具有一定的生命期,是暂时存在的;而程序则是一组代码的集合,它是永久存在的,可长期保存。
  • 一个进程可以执行一个或几个程序,一个程序也可以构成多个进程。进程可创建进程,而程序不可能形成新的程序。

二、线程

2.1 定义

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID、当前指令指针(PC)、寄存器集合和堆栈(stack)组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的资源。

线程共享的进程环境包括:进程代码段、进程的共有数据(如全局变量,利用这些共享的数据,线程很容易的实现相互之间的通信)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。

线程拥有这许多共性的同时,还拥有自己的个性。有了这些个性,线程才能够实现并发性。这些个性包括:

  • 线程ID:每个线程都有自己的线程ID,这个ID在本进程中是唯一的。进程用此来标识线程;
  • 寄存器的值:由于线程间是并发运行的,每个线程有自己不同的运行线索,当从一个线程切换到另一个线程上时,必须将原有线程的寄存器集合的状态进行保存,以便将来该线程在被重新切换时能得以恢复。
  • 线程的堆栈(Stack):堆栈是保证线程独立运行所必须的。线程函数可以调用函数,而被调用函数中又是可以层层嵌套的,所以线程必须拥有自己的函数堆栈,使得函数调用可以正常执行,不受其他线程的影响。在一个进程的线程共享堆区(heap)。
  • 错误返回码
  • 线程的信号屏蔽码
  • 线程的优先级

一个线程可以创建和撤销另一个线程,同一进程的多个线程之间可以并发执行。由于县城之间的相互制约,致使线程在运行中呈现间断性。

线程也有就绪、阻塞和运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

引入线程后,进程的内涵发生了变化,进程只作为除CPU以外系统资源的分配单元,线程则作为处理器的分配单元。在同一进程中,线程的切换不会引起进程的切换,但从一个进程中的线程切换到另一个进程中的线程时,将会引起进程的切换。

1.2 进程与线程的区别

  • 调度:在传统操作系统中,拥有资源和独立调度的基本单位都是进程。引入线程后,线程是独立调度的基本单位,进程是拥有资源的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行的线程切换,则会引起进程切换。
  • 拥有资源:不论是传统的还是引入线程的操作系统,进程都是拥有资源的基本单位,线程不拥有资源(也有一点必不可少的资源),但线程可以共享其隶属进程的系统资源。
  • 并发性:在引入线程的操作系统中,不仅进程可以并发执行,而且同一进程内的多个线程也可以并发执行,从而使操作系统具有具有更好的并发性,大大提高了系统的吞吐量。
  • 系统开销:创建和撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O设备等等,因此操作系统所付出的开销远大于创建或撤销线程的开销。类似地,在进程切换时,涉及当前执行进程CPU环境的保存以及新调度的进程CPU环境的设置;而线程切换时只需保存和设置少量寄存器内容,因此开销很小。另外,由于同一进程内的多个线程共享进程的地址空间,因此这些线程之间的同步与通信比较容易实现,甚至无需操作系统的干预。
  • 地址空间和其他资源(如打开的文件):进程的地址空间之间相互独立,同一进程的各线程间共享进程的资源,某进程内的线程对于其他进程不可见。
  • 通信方面:进程间通信需要借助操作系统,而线程间可以直接读、写进程数据段(如全局变量)来进行通信。

三、进程通信与进程同步

多个进程可以共享系统中的各种资源,但其中许多资源一次为能为一个进程使用,我们把一次仅允许一个进程使用的资源成为临界资源。许多物理设备都属于临界资源,如打印机等。

对临界资源的访问,必须互斥的进行,在每个进程中,访问临界资源的那段代码成为临界区(Critical Section)。

进程通信与同步有如下一些目的。

  • 数据传输
  • 共享数据
  • 通知数据
  • 资源共享
  • 进程控制

Linux进程间通信的几种主要手段简介:

  • 管道(Pipe)及有名管道(named Pipe)
  • 信号(Signal)
  • Message(消息队列)
  • 共享内存(Shared Memory)
  • 信号量
  • 套接口

Linux线程间通信:互斥体(互斥量)、信号量、条件变量
Windows进程间通信:管道、共享内存、消息队列、信号量、socket
windows线程间通信:临界区(Critical Section)、互斥量(Mutex)、信号量(信号灯)(Semaphore)、事件(Event)。

四、调度算法

调度的基本准则包括CPU利用率、系统吞吐量、周转时间、等待时间、响应时间等。

  • 系统吞吐量:表示单位时间内CPU完成作业的数量
  • 周转时间:作业完成时刻减去作业到达的时刻
  • 等待时间:进程处于等处理器状态的时间之和,等待时间越长,用户满意度越低。
  • 响应时间:从用户提交请求到系统首次产生响应所用的时间。

典型的调度算法包括:

实时系统中:FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。

交互式系统中:RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。

多级反馈队列调度算法。其中SJF的平均等待时间、平均周转时间最少。

五、死锁

所谓死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。现实生活中简单的例子:交通阻塞,两股相向而行的车流都想通过已被对方占用的道路,结果双方都不能前进。

死锁产生的原因:系统资源的竞争、进程推进顺序非法

死锁产生的必要条件:产生死锁必须同时满足以下四个条件,只要其中任一条件不满足,死锁就不会发生。

  • 互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待;
  • 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他资源强行夺走,即只能由获得该资源的进程自己来释放。
  • 请求和保持条件:又称为部分分配条件。进程每次申请它所需要的一部分资源,在等待新资源的同时,进程继续占有已分配到的资源。
  • 循环等待条件:存在一种进程资源的循环等待链,链中每个进程已获得的资源同时被链中下一个进程所请求。即存在一个处于等待状态的进程集合$\{P_1,P_2,….P_n\}$其中$P_i$等待的资源被$P_{i+1}(i=0,1,2,…n-1)$占有,$P_n$等待资源被$P_0$占有。

死锁处理策略

  • 预防死锁:设置某些限制条件,破坏产生死锁的四个必要条件中的一个或几个
  • 避免死锁:在资源的动态分配过程中,用某种方法防止系统进入不安全状态。银行家算法是著名的死锁避免算法。
  • 死锁的检测及解除:无需采取任何限制性措施,允许进程在运行过程中发生死锁,通过系统的检测机制及时地检测出死锁的发生,然后采取某种措施解除死锁。死锁可利用资源分配图来描述。死锁的解除主要方法:资源剥夺法、撤销进程法、进程回退法


应统联盟


连接十万名应统专业同学


阿药算法


打通算法面试任督二脉