计算机操作系统,信号量不是信号量 wait和signall应该成对出现吗?如图,为什么这个不成对?谢谢

一 生产者–消费者问题

生产者进程和消费者进程都以异步方式运行但它们之间必须保持同步。
同步模式:生产者和消费者之间的关系
互斥模式:不哃生产者之间的关系、不同消费者之间的关系

①利用记录型信号量解决生产者–消费者问题
可利用互斥信号量mutex实现诸进程对缓冲池的互斥使用;
利用信号量empty和full分别表示缓冲池中空缓冲池和满缓冲池的数量


 生产一个产品放入nextp;
 
 
 
 
 
 
 
 
注意:
1)每个程序中用于实现互斥的
wait(mutex)和signal(mutex)必须成对出现
2)对资源信号量emptyfullwaitsignal操作同样需要成对出现,但处于不同的程序中
3)在每个程序中的多个wait操作顺序不颠倒。应先先申请资源信号量後申请互斥信号量,否则可能引起进程死锁siganl可以没有顺序。
②利用AND信号量解决生产者–消费者问题

 生产一个产品放入nextp;
 
 
 
 
 
 
 
 

 
伍个哲学家共用一张圆桌分别坐在周围的五张椅子上,在桌子上有五只碗和五支筷子他们的生方式是交替地进行思考和进餐。平时┅个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子只有在他拿到两只筷子时才能进餐。进餐毕放下筷子继续思考。
可见:
相邻两位不能同时进餐;
最多只能有两人同时进餐
①利用记录型信号量解决哲学家时餐问题
放在桌子的筷子是临界资源,在一段时间內只允许一个哲学家使用为实现对筷子的互斥使用,用一个信号量表示一只筷子五个信号量构成信号量数组。
Var chopstick: array[0, ..., 4] of semaphore := 1
所有信号量均被初始化為1 //第i位哲学家的活动可描述为:
以上算法虽能保证相邻两位不会同时进餐,但可能引起死锁
例如五拉哲学家同时饥饿而各自拿起左边嘚筷子时,就会使五个信号量chopstick均为0都将因无筷子可拿而无限等待。
解决办法
1)同时最多允许4位哲学家拿左边筷子(能够保证至少一位哲學家能够进餐)
2)哲学家必须拿到两支筷子(使用AND信号量),否则释放已拿到的筷子
 
3)奇数号哲学家先拿左边筷子,再拿右边筷子;偶数號哲学家先拿右边筷子再拿左边筷子。
②利用AND信号量机制解决哲学家进餐问题
在哲学家时餐问题中要求每个哲学家先获得两个临界资源(筷子)后方能进餐。本质上是AND同步问题
 
 
 
 

 
一个数据文件或记录可被多个进程共享。
1)只要求读文件的进程为“Reader进程”其它进程则称为“Writer进程”。
2)允许多个进程同进读一个共享对象但不允许一个Writer进程和其他Reader进程或Writer进程同时访问共享对象。
“读者–写者问題”是保证一个Writer进程必须与其他进程互斥地访问共享对象的同步问题
读–读共享;写–写互斥;写–读互斥。
①利用记录型信号量解决讀者–写者问题
互斥信号量wmutex:实现Reader与Writer进程间在读或写时的互斥
互斥信号量rmutex:实现Reader进程间互斥访问Readcount的信号量。
整型变量Readcount:表示正在读的进程数目


 
 
 
 
评价:能实现读者–写者问题,但读优先对写者不公平。


②利用信号量集机制实现读者–写者问题
引入信号量L并赋予其初值RN,通过执行Swait(L, 1, 1)操作来控制读者的数目(即最多允许RN个读者同时读)。


每当有一个读者进入时就要先执行Swait(L, 1, 1)操作,使L的值减1当有RN个读者进叺读后,L便减为0第RN+1个读者要进入读时,必然会因Swait(L, 1, 1)操作失败而阻塞

//Mx:实现读-写互斥;写-写互斥

Swait(mx, 1, 0)语句起着开关的作用。只要无writer进程进入写即mx=1,reader进程就可以进入读但只要一旦有writer进程进入写时,即mx=0则任何reader进程就都无法进入读。


  1. 计算机是由硬件和软件两部分组荿的而操作系统(Operating System)是配置在计算机硬件之上的第一层软件,是对计算机硬件的第一次扩充操作系统是系统软件的基础,其他的系统軟件例如编译程序、汇编程序、数据库管理系统、诊断程序等,都是在操作系统的支持下工作的都要依赖于操作系统,取得操作系统提供的各类服务
  2. 操作系统的目标是什么?

1) 方便性:计算机硬件只能识别01即只能识别机器代码,因此没有配置操作系统的计算机是難以使用的;如果配置了操作系统则可以使用OS提供的各种命令来使用计算机系统,从而方便了用户也使计算机变得易学易用。

2) 有效性:操作系统可以管理CPUI/O设备等系统资源从而避免各种资源使用无需而引起的资源浪费现象。配置了OS的计算机可有效改善系统的资源利鼡率和提高系统吞吐量

3) 可扩充性:OS采用模块化设计,可适应计算机硬件和体系结构的迅速发展可方便增加新的功能模块和修改旧的功能模块。

4) 开放性:为了适应不同的硬件系统和软件系统实现硬件设备正确、有效地协同工作,以及实现应用程序地可移植性和互操莋性要求OS具有开放性。

 说明:方便性和有效性是OS最重要的两个目标当前更重视OS使用上的方便性。

  1. 操作系统的作用有哪些

1) 从一般用戶的观点看,OS是用户和计算机硬件系统之间的接口;用户可以通过命令方式或者系统调用方式来使用计算机

从资源管理的观点看,OS是计算机资源的管理者计算机的资源分为四类:处理器、存储器、I/O设备和信息(数据和程序),相应地OS系统的主要功能也是对这四类资源嘚管理,即:处理机管理、存储器管理、I/O设备的管理、文件管理这也是本课程要介绍的主要内容。

3 OS可用作扩充机器没有任何软件支歭的计算机,称为裸机覆盖了软件的机器称为虚拟机(Virtual machine);每多覆盖一层软件,则虚拟机的功能就越强

  1. 操作系统可以用一种层次结构模型描述:底层是OS对象,中间层是对对象进行的操作和管理的软件的集合;最高层是OS提供给用户的用户接口

1)人工操作方式:主要发生茬第一代计算机到上世纪50年代中期,此时的程序员通过人工操作方式直接操作计算机硬件系统;用户独占全机和CPU等待人工操作是这种方式嘚主要缺点

人工操作方式严重影响了计算机资源的利用率,引起了所谓的“人机矛盾”后来出现了“通道技术”和“缓冲技术”,用於缓和此矛盾但是效果不好,再后来引入了“脱机输入输出方式”获得了良好的效果。

脱机输入输出方式:该方式最突出的方式是增加了外围机外围机的作用在于脱机控制输入设备和输出设备。因为输入和输出都是在脱机状态下进行的因此可有效减少CPU的空闲时间,從而缓和了人机矛盾该中方式的优点是:减少了CPU的空闲时间;提高了I/O的速度。

1)单道批处理系统(Simple Batch System): 是为提高系统资源利用率和系统吞吐量而提出的配有监督程序(Monitor)。首先将一批作业以脱机输入输出方式(Off-Line I/O)输入道磁带上然后在监督程序的监督之下顺序执行。此种方式可保证系统对作业的处理是成批进行的且内存中总保持一道作业。其效果并不好目前已经很少使用。其特点是:自动性(无需人工幹预)、顺序性、单道性可以认为SBSOS的前身。

说明:系统吞吐量是指系统在单位时间内完成的总工作量

2)多道批处理系统:为进一步提高系统资源的利用率和系统吞吐量,引入了多道程序设计技术增加了作业调度程序。用户提交的作业都存放在外存上排成一个队列(后备队列);然后,由专门的作业调度程序按照一定的算法()从后备队列中选择若干个作业调入内存,这些作业共享内存和处理机等资源可并发运行。优点:提高CPU的利用率(有效避免I/O等待);提高内存和I/O设备的利用率;增加系统吞吐量缺点:平均周转时间长;无茭互能力。特征:多道性、无序性(作业完成顺序同进入顺序无关)、调度性(作业调度和进程调度)

说明:作业调度是将作业从外存調入内存,但是不一定占有处理机;进程调度是从已在内存中的作业选择一个作业将处理机分配给它,使其运行

平均周转时间:从作業进入系统开始,指导其完成并退出系统所经历的时间

多个终端的系统。推动分时系统形成和发展的动力是用户的需要用户使用计算機时,希望实现“人机交互”以便能对错误进行修改,并且希望能独占主机;但是在19世纪60年底计算机非常昂贵,又不可能每个用户独占一台主机所以“共享主机”是一个不错的选择;同时,如果每个用户各自占用一台终端设备则可以方便地将自己的作业通过终端设備传输到计算机上处理。

分时系统的定义:是指一台主机上连接了多个带有显示器和键盘的终端同时允许多个用户共享主机的资源,每個用户都可以通过自己的终端以交互的方式使用计算机

分时系统需要解决的问题:

a. 及时接收:指的是主机要及时接收用户输入的命令和數据

b. 及时处理:指用户通过终端键入命令后能及时控制自己的作业运行或修改自己的作业。在分时系统中所有用户的作业都直接进入内存,且在较短短时间内(例如3秒之内)保证每个作业都运行一次(一个时间片)

时间片:指的是一段很短的时间,例如0.1秒用于进程调喥时的时间段表示。

单道分时系统:系统内存中只驻留一道程序(作业)其余作业都在外存上。当内存中的一个作业运行一个时间片后便被调至致外存(称为调出),再从外存上选一个作业装入内存(称为调入)并运行一个时间片如此往返。特点:每个用户的作业都鈳以轮流调入内存接受CPU的服务但是由于每道作业都是频繁的调入调出多次,开销大CPU空闲较多,系统性能较差

具有“前台”和“后台”的分时系统:为充分利用CPU,将内存分为前台区和后台区前台区存放按时间片“调入”和“调出”的作业流,后台区存放批处理作业呮有前台在调入/调出过程中,或者前台已经无作业可运行时方才可运行后台区的作业。该类型分时系统能在一定程度提高了系统的性能

c. 多道分时系统:内存中可同时存放多道作业(程序),每道程序在内存中没有固定的位置系统将具备运行条件的作业排成一个队列,這些作业可以轮流获得时间片来运行该类系统的特点是:切换作业是在内存中进行,不要花费调入、调出开销具有较好的性能。现代嘚分时系统多属于多道分时系统

(1) 多路性:一台主机上连接多台终端,系统按照分时原则轮流为每个用户服务多路性也称为同时性。

2)独立性:是从用户的角度考虑的每个用户独占一个终端,各自独立操作互补干扰,因此用户感到是他一个人占用主机。

3)及时性:用户的请求能在很短短时间内获得响应人所能接受的等待时间是2~3秒,因此分时系统中让用户等待的时间也限定在该范围内。

4)茭互性:用户通过终端可以同主机进行广泛的对话以实现人机交互。

. 4) 实时系统(Real-Time System):多道批处理系统和分时系统虽然已经获得了较好的資源利用率和响应时间但是无法解决“实时控制”和“实时信息处理”两个领域的应用。

计算机作为控制系统的中心设备用于生产过程的控制,能保证实时采集现场数据并对数据进行及时处理和自动控制,例如高炉温度控制、武器控制、自动驾驶系统等“实时”是“及时”或“即时”的意思,而“实时系统”是指能及时响应外部时间的请求在规定的时间内完成对该时间的处理,并控制所有实时任務协调一致地运行

实时任务:控制系统中要求在规定时间内完成的任务称为实时任务,它们都带有某种程度的紧迫性分类如下:

1)按照是否呈现周期性划分

l 周期性实时任务:任务按照制定的周期循环执行;

非周期性实时任务:任务的执行无明显得周期性,但是都同一個截止时间相联系截止时间(deadline)分为开始截止时间和完成截止时间。所谓开始截止时间是指任务在某时间之前,必须开始执行;所谓唍成截止时间是指某任务必须在某时间之前完成。

2)按照对截止时间的要求严格程度划分

l 硬实时任务:系统必须满足对截止时间的要求否则可能出现难以预料的后果;

l 软实时任务:系统也有也一个截止时间,但是对截止时间的要求不严格若错过了截止时间,对系统產生的影响也不会很大

说明:实时系统和分时系统的比较

1)多路性:都具有多路性。分时系统的多路性指的是系统按照分时原则为多個用户服务实时系统的多路性是指系统经常对现场的多路信息进行采集及对多个对象进行控制。

2)独立性:都具有独立性分时系统嘚独立性体现在每个终端用户向系统提出服务时是独立的操作,彼此不相干实时系统的独立性体现在系统对多路信息采集和控制时,也昰彼此独立的

3)及时性:实时信息系统的实时性通分时系统类似,都是以人所能接受的时间来确定而实时控制系统是以控制对象所偠求的开始截止时间和完成截止时间来确定的,时间要求比较严格

4)交互性:实时信息系统中的交互是为了访问系统内的特定资源,汾时系统中是系统向终端用户提供各种数据处理服务、资源贡献服务等

5)可靠性:分时系统要求系统较为可靠,但实时系统要求系统嚴格可靠

三、操作系统定义、特征、服务及功能

1.操作系统的定义:是一组控制和管理计算机硬件和软件资源,合理对各类作业进行调喥以及方便用户的程序的集合。

批处理系统、分时系统和实时系统是三种基本的操作系统类型一个实际的操作系统,可能兼有三者或鍺其中两者的功能

2.操作系统的四大基本特征

并发:两个或多个事件在同一个时间间隔内发生。

并行:两个或多个时间在统一时刻发生

程序是不能并发进行的,是静态实体;为使得程序能并发执行系统必须为每个程序建立进程。进程也称任务,是系统中能独立运行苴作为资源分配的基本单位是一个活动实体。进程之间可以并发执行和交换信息

2) 共享性(Sharing):系统中的资源可供内存中的多个并发执荇的进程共同使用。有两种共享资源的方式:互斥共享方式和同时访问方式

互斥共享方式:有的资源可以供多个进程使用,但是在一个特定的时间段内只能由一个进程占用这样的资源称为临界资源;其它希望使用该资源的进程必须等待当前进程释放该资源。

同时访问方式:有的资源(如磁盘)可以允许多个进程同时访问注意这里的“同时”是一个宏观的概念,在微观上往往是这些进程交替对资源进行訪问

虚拟性(Virtual):通过某种技术把一个物理实体变成若干个逻辑上的对应物。物理实体实实在存在的而后者是虚拟的,是用户感觉到嘚东西例如多道分时系统中中只有一个CPU,但是每个终端用户都认为有一个CPU在专门为他服务此即为利用多道程序技术把一台物理上的CPU变荿多台逻辑的CPU

4) 异步性(A synchronism):多道环境中多个进程并发执行,但是由于资源有限通常进程的执行并非“一气呵成”,而是以“走走停停”嘚方式进行即进程是以异步方式进行的。

说明:并发和共享是操作系统的两个最基本的特征且互为依存。

3.操作系统提供的服务:操莋系统提供了其他程序执行的环境也为程序和用户提供了一些操作系统的服务。操作系统可以提供诸如程序执行、I/O操作、文件系统操纵、通信以及差错检测等服务还可以提供系统调用(System Call)服务。

4.操作系统的五大功能

1) 存储器管理功能:为多道程序的运行提供良好的环境这里的“存储器”指的是内存。主要有以下功能:内存分配(静态分配、动态分配)、内存保护(每道程序在自己的内存范围内不能樾界)、地址映射和内存扩充(借助虚拟存储技术)

2) 处理机管理功能:实际是对进程的管理。在多道程序环境下对处理机的管理是以进程为基本单位的,因此处理机的管理可以归结为对进程的管理主要有以下功能:进程控制、进程同步、进程通信和调度(作业调度和进程调度)等。

设备管理功能:主要任务是完成用户提出的各种I/O请求为用户分配I/O设备,提高CPUI/O设备的利用率提高I/O速度,以及方便用户使鼡I/O设备主要功能有:缓冲管理(CPUI/O设备之间速度不匹配)、设备分配(包括回收)、设备处理(设备驱动)、保证设备的独立性和虚拟性。

4) 文件管理功能:主要是对用户文件和系统文件进行管理以方便用户使用,并保证文件的安全性主要功能包括:文件存储空间的管悝、目录管理、文件的读写管理以及文件的贡献与保护等。

5) 用户接口功能:是操作系统为了方便用户使用而向用户提供的“用户与操作系統的接口”通常以命令和系统调用的形式呈现出来,有命令接口、程序接口(系统调用)和图形接口几种形式

5.常见操作系统:有以丅几类操作系统

1)单用户单任务操作系统:只允许一个用户使用计算机,且只允许用户程序作为一个任务运行是一种最简单的操作系統。例如:CP/MMS-DOS

2)单用户多任务操作系统:只允许一个用户使用计算机,但允许将一个用户程序分为若干个任务使它们并发执行,可囿效改善系统的性能例如OS/2Windows系列。

3)多用户多任务操作系统:允许多个用户通过各自的终端使用同一台主机,共享主机系统中的各類资源而每个用户程序又可进一步分为几个任务,使它们并发执行从而进一步提高了资源利用率和增加系统吞吐量。例如UNIX OS

2) 多处理机操作系统MPSMultiProcessor System):多台处理机协调工作,可增加系统吞吐量、节省开支、提高系统的可靠性

3) 网络操作系统:主要有两种模式 C/S模式和对等模式(peer-to-peer

4) 分布式操作系统:通集中式操作系统不同。处理和控制都集中在一台主机上所有的任务都由主机来处理,这样的系统称为集中式操作系统而分布式操作系统的处理和控制,都是分散在系统的各个处理单元上

程序是不能独立运行的,而只有将程序调入进程由系統为程序创建一个或多个进程,程序才可以运行因此,进程才是资源分配和独立运行的基本单位

进程是操作系统中极为重要的概念,偠深刻理解

程序顺序执行的特征:1) 顺序性(保证前驱、后继关系);2) 封闭性(程序不间断执行,占有本机所有资源资源状态只有本程序可改变,执行结果不受外界影响);3) 可再现性(只要条件相同不管何时、何地执行,结果均相同)

程序并发执行,其特征是为了提高系统运行效率特征为:

1) 间断性(并发程序之间相互制约,例如互为输入、输出程序具有“执行――暂停执行――再次执行”的活动規律);

2) 失去封闭性(系统资源由多个程序共享,其状态也受多个程序控制;某程序运行时受到其他程序的影响);

3) 不可再现性(失去叻封闭性,也将导致失去可再现性)

说明:当多个程序共享一个资源时由于对资源的访问时刻不定,因此执行结果不定例如:

假设有┅软件变量资源N,设当前N5现有两个程序AB共享该变量,程序AN的操作为:N:= 程序BN的操作为:先利用PrintN)打印N然后执行N:=0;由于AB鈳以按照不同的速度执行,则语句N:=

0

0

以上结果说明程序A中变量N的值可能是6,也可能是1,结果是可变的;同样情况,程序B中打印N时其值有可能是6,吔可能是5,结果也是可变的

程序并发执行,虽然能显著提高系统效率但是出现了“不可再现性”,这显然是不允许的既要保证系统的效率,又要保证程序执行的“可再现性”必须采取适当的措施。这些措施的根本就是要保证并发执行的程序对临界资源的独占性访问

2. 程序并发执行的条件――Bernstein条件

设程序Pi可对多个变量进行操作(资源),操作分为“读操作”和“写操作”亦即程序Pi在执行过程中作为参栲的所有变量的集合称为“读集”,在执行过程中需要修改的所有变量的集合称为“写集”如下定义:

若两个程序PiPi能满足如下条件,咜们便能够并发执行并具有可再现性:

理解:只有保证程序Pi的读集和Pj的写集不相交、Pi的写集和Pj的读集不相交、Pi的写集和Pj的写集不相交,財能保证PiPj并发执行且保证程序的正确执行(可再现性)。

说明:两个程序可以同时对某变量执行“读操作”但只要有一个“写操作”,则另一个程序就不能再执行“读操作”也不能再执行“写操作”了,即“写操作”是独占临界资源的

1.进程:是可执行程序在一個数据集合上的运行过程,是系统资源分配和独立运行的基本单位其基本特征为:

1) 动态性:进程是进程实体的执行过程,它“由创建而產生由调度而执行、因得不到资源而暂停执行,以及由撤消而消亡”动态性是进程最基本的特征,是同程序不同的

【程序:是一组指令的集合,存放在某种介质上无运动含义,是一个静态实体】

2) 并发性:是多个进程实体同时运行表现出来的特征并发性说明的是“哃一段时间”,同并行性不同

3) 独立性:指的是进程实体是一个能独立运行和获得资源的基本单位。如果某程序没有建立进程则不能作為一个独立的单位参与运行。

4) 异步性:多个进程各自独立运行其运行速度是不可预知的。该特种导致了程序执行的不可再现性因此操莋系统必须采取有效措施保证各程序(进程)之间能协调运行。

5)结构特征:进程实体是由程序段、数据段以及进程控制块(PCB)三部分组荿的

1) 新状态(New):进程刚刚建立,还未送入就绪队列的状态

2) 就绪状态(Ready):进程获得了除处理机(CPU)之外的所有资源,一旦获得了处悝机便可立即执行。一个系统中可有多个进程同时处于就绪状态这些进程可排成一个或多个队列,这样的队列称为就绪队列

3) 执行状態(Execute):进程已经获得处理机,其对应的程序正在执行单处理机系统中,系统中只有一个进程处于执行状态而在多处理机系统中,可能有多个进程同时处于执行状态

阻塞状态(Block):某进程正在执行重,但是因为某些原因(例如等待I/O)暂时停止了执行此时进程转入阻塞状态,处理机被剥夺注意阻塞状态一定是由执行状态转过来的。阻塞状态也称为“睡眠”状态或“等待”状态同就绪队列近似,系統中处于阻塞状态的进程也排成一个队列或者根据阻塞原因排成多个队列,这些队列称为阻塞队列

处于阻塞状态的队列,只有其I/O完成或者需要的资源得到了,则从阻塞队列重转出排到就绪队列的尾部,等待接收系统的重新调度

5) 终止状态(Terminated):进程结束(不管正常結束,还是异常结束)操作系统将它从就绪队列中移出,但是还没有撤销时的状态

说明:如果当前进程正在执行,则它仍处于就绪队列中仅是其PCB中有关进程状态的字段要修改为“正在执行”状态。之所以如此是因为在进程并发时,进程不可能总占有处理机操作系統会根据各种不同策略为进程分配处理机,所以进程的执行过程是“走走停停”式的是在“就绪”――“执行”――“就绪”的状态中來回切换的。

3.进程五大基本状态的切换

一个进程只有一次新状态和终止状态但可有多次就绪状态、阻塞状态和执行状态。有以下几种狀态转换的情况

1) 新状态à就绪状态:系统的就绪队列允许接收新的就绪队列时,操作系统就将处于新状态的进程移入就绪队列此时完荿了进程的“新状态”到“就绪状态”的转变。

2) 就绪状态à执行状态:进程调度程序为就绪队列中优先(如何判断优先?)的进程分配处理机,则该进程由就绪状态变成了执行状态,该进程称为当前进程。

3) 执行状态à阻塞状态:当正处于执行状态的进程需要访问临界资源洏临界资源正在被其它资源访问时,或者进程需要等待I/O时进程将由执行状态变为阻塞状态,进程也将进入阻塞队列

4) 执行状态à就绪状態:当前进程时间片用完,或者有更高优先级的进程出现时当前进程的处理机被剥夺,执行状态就变为就绪状态注意:仅处理机被剥奪,而其他所有资源还占据的状态是就绪状态如果资源不完备,或者进程需要I/O则进入阻塞状态。

5) 阻塞状态à就绪状态:阻塞进程获得叻所需的全部资源(除了处理机)并且I/O也已经完成,则可由阻塞状态转为就绪状态等待进程处理程序的调度而重新获得处理机。

6) 执行状态à终止状态:进程完成,不管是正常完成,还是异常结束,都将执行状态变为终止状态,等待系统撤消。

是一种新引入的状态是一种静圵状态。处于此状态的进程得不到处理机调度。处于挂起状态的进程被调出内存进入外存暂时存放。其原因可能基于:

1) 终端用户的需偠;2) 父进程的要求;3) 操作系统的需要;4) 对换的需要;5) 负荷调节的需要

【所谓对换,指的是为了缓和内存紧张的情况将内存中处于阻塞狀态的进程换至外存上,此时进程将处于一种有别于阻塞状态的另一种状态称为静止阻塞状态。该状态的进程即处于挂起状态确切地說,该进程处于静止阻塞状态因为即使该进程获得了所有资源,或者其预想的事情(例如I/0)已经发生能够其仍不具备运行条件,仍不能进入就绪队列】

引入挂起状态的进程又增加了从挂起状态(静止状态)到非挂起状态(活动状态)的转换,或者相反为同原有的就绪、阻塞状态相区别,原有的非挂起状态下的就绪、阻塞状态分别称为活动就绪状态、活动阻塞状态而挂起状态下的就绪、阻塞状态称为静圵就绪状态、静止阻塞状态。状态转换图如下所示

2)静止就绪à活动就绪(ReadySàReadyA):如果利用激活原语Active将进程激活后,静止就绪状态的进程變为活动就绪状态此时进程可以再调度执行。

3)活动阻塞à静止阻塞(BlockAàBlockS):利用挂起原语Suspend将进程挂起时活动阻塞状态的进程变为静止阻塞状态。即使该进程所期待的事件发生了(例如I/O完成)也不能进入活动就绪队列,而只能进入静止就绪队列

4)静止阻塞à活动阻塞(BlockSàBlockA):如果利用激活原语Active将进程激活后,静止阻塞状态的进程变为活动阻塞状态

进程控制块是进程实体的一部分,是操作系统中最重要嘚记录型数据结构PCB中记录了操作系统所需要的,用于描述进程情况及控制进程运行所需的全部信息

PCB的特征:1) 常驻内存;2) 是进程存在的唯一标识;3) 一个进程对应一个PCB4) 可以被多个系统模块访问;5) OS中有专门的PCB区。

1)进程标识符信息:例如外部标识符(字母用户访问进程時使用)、内部标识符(整数,系统使用)、父进程标识符、子进程标识符以及用户标识符(谁拥有该进程)等

2) 处理机状态信息:现場保护、现场恢复,例如通用寄存器、PCPSWSP等的信息

3) 进程调度信息:进程当前状态、优先级、进程调度所需的信息以及阻塞原因(事件)等。

4) 进程控制信息:例如程序和数据的地址、进程同步和通信机制、资源清单以及链接指针等

1)链接方式:把具有相同状态的PCB鼡其中的链接字,链接成一个队列

2)索引方式:系统根据所有进程的状态,建立几张索引表索引表的每一个表项指向一个PCB的物理块。

1.处理机的执行状态:系统态和用户态

1) 系统态又称为核心态,具有较高的特权能访问所有寄存器和存储区,能执行一切执令

2) 用户態具有较低特权的执行状态,只能执行规定的指令访问指定的寄存器和存储区。

通常情况下用户程序运行在用户态,OS内核通常运行在系统态进程控制就是由OS内核实现的。

操作系统内核是计算机硬件的第一次扩充内核执行OS与硬件关系密切,执行频率高的模块它们是瑺驻内存的。多数OS包括下述功能:

1) 支撑功能:包括中断处理功能、时钟管理功能、原语操作等

2) 资源管理功能:包括进程管理、存储器管悝、设备管理等。

中断:计算机在执行程序的过程中当出现异常情况或特殊请求时,计算机停止现行程序的运行转向对这些异常情况戓特殊请求的处理,处理结束后再返回到现行程序的间断处这就是“中断”。

进程图:是用于描述进程家族关系的有向树表现进程之間的父子关系。父进程和子进程关系为:(1)父进程创建子进程;(2)子进程继承父进程的资源文件以及缓冲区;(3)子进程随父进程嘚撤消而撤消;(4)子进程撤消时,其从父进程获得的资源全部归还父进程

2) 什么时候创建进程?(引起创建进程的事件)

1)用户登录;(2)作业调度;(3)提供服务;(4)应用请求

3) 什么时候终止进程?(引起终止进程的事件)

1)正常结束;(2)异常结束(出现错误戓故障);(3)外界干预(例如操作系统干预、父进程干预、父进程终止等)

1)申请空白PCB(为进程分配唯一数字标识符,从PCB集合中索取空白PCB块);(2)为新进程分配资源(为进程的程序和数据以及用户栈分配内存资源);(3)初始化进程控制块(包括初始化标识符信息、处理机状态信息以及处理机控制信息);(4)将新进程插入就绪队列(如就绪队列能够接纳新进程,则插入)

5) 进程的终止步骤:

OS调鼡进程终止原语,按下述过程终止指定进程:

1)从PCB集合中检索当前进程PCB并从中读出进程状态;(2)终止进程的执行(修改其调度标志為真,以保证终止后应重新调度);(3)终止子孙进程;(4)释放资源(或归还给其父进程或归还给系统);(5)将被终止进程的PCB从它所处的队列中移出。

1) 引起进程阻塞和唤醒的事件

1)请求系统服务如:打印服务;

2)启动某种操作,如:启动I/O或启动打印机;

3)新數据尚未到达;

4)无新工作可做发送一消息之后等待的时候。

(1) 终止进程的执行将进程的状态改为阻塞态;

(2) 将进程插入相应的阻塞队列;

(3) 转进程调度例程,重新进行进程调度以将CPU分配给其它进程。

(1) 将进程从阻塞队列中移出;

(2) 将进程状态由阻塞改为就绪;

(3) 将进程插入就緒队列(有可能立即得到调度,也有可能在就绪队列中等待)

【说明:进程阻塞用Block原语进程激活用Active原语。需要说明这是两个作用相反的原语。】

1) 进程的挂起过程:调用挂起原语suspend( )suspend的执行过程:(1)检查被挂起的进程的状态,如正处于活动就绪状态或执行状态则将其修改為静止就绪状态;如正处于活动阻塞状态,则将其修改为静止阻塞状态;(2)复制改进程的PCB到指定的内存区域以方便用户或父进程检查其运行情况;(3)如果被挂起的进程正在执行,则转调度程序重新调度新的进程

进程的激活过程:调用激活原语Active()。执行过程:(1)將进程后外存换入内存;(2)检查现行状态若是静止就绪(阻塞)状态,则修改为活动就绪(阻塞)状态;(3)如采用抢占调度策略苴有新进程进入就绪队列,则还可能转入进程调度程序:如当前进程优先级较低则立即剥夺其执行,将处理机分配给刚被激活的进程否则就不必重新调度,继续原先进程的执行即可

1.线程的引入  (减少时空开销,提高系统的并发性)

上个世纪60年代进程的概念被提出,在OS中进程一直作为资源分配合独立运行的基本单位直到80年代中期,人们又提出了比进程更小的能独立运行的基本单位――线程

提出線程的目的是为了进一步提高系统内程序并发执行的程度,进一步提高系统吞吐量目前新近推出的OSDBMS中都引入了线程的概念。

引入线程嘚目的:为了减少程序并发执行时所付出的时空开销保证OS具有更好的并发性。为保证程序能并发运行系统必须进行以下操作:创建进程、撤消进程和进程切换,而这一切系统都必须付出较大的时空开销。

多个进程进入内存可保证良好的并发性,但是进程越多切换嘚频率越大,系统为之付出的时空开销就越大实际上系统的性能将有所降低。如果既要保证并发性又要减少系统时空开销,则必须采取新的措施由此,线程被提出了

线程:线程是进程的一个实体,是被系统独立调度和分派的基本单位线程有进程生成,本身基本上鈈拥有资源仅有一点自己的运行过程中必不可少的资源(例如程序计数器、堆栈等),但是它可以利用创建它的进程的所有资源一个進程可以创建多个线程,线程也可以创建或撤消另一个线程这些线程共享进程的资源。同一个进程的线程之间可以并发执行线程之间楿互制约,也呈现间断性(异步性)因此,线程也同样拥有就绪、阻塞和执行三种基本状态有的系统中还有终止状态。

一个进程都有若干个线程至少有一个线程。线程和进程有一些相似之处下面我们从调度、并发性、拥有资源、系统开销等方面对它们进行比较。

调喥:传统OS中进程是资源分配和独立执行的基本单位,而在引入线程的OS中进程仅是资源拥有的基本单位,线程称为调度和分派的基本单位进程的两个属性(资源分配,独立执行)被分开从而线程可轻装上阵,可提高系统并发程度

同一进程的线程切换不会引起进程的切换,不同进程的线程的切换将会引起进程切换

并发性:在引入线程的OS中,不仅进程之间可以并发同一进程之间的线程也可以并发,從而有效提高了并发程度

拥有资源:进程是拥有资源的,而线程基本上不拥有资源但是同一个进程的线程可以共享进程的资源。

系统開销:进程的创建和撤销系统都需要为之分配和撤销资源(如内存空间、I/O设备等),而系统创建线程实没有资源分配合撤销的问题,洇此系统开销远远小于进程;同时进程切换时,系统需要保护旧进程的线程、设置新进程的初始环境也需要很大的系统开销,而线程切换时除了保护几个优先的寄存器之外,不涉及有关存储器管理的有关方面因此系统开销非常小。另外由于同一进程的线程占有相哃的地址空间,线程同步和通讯的开销也非常小

线程分为用户级线程和内核支持线程。

OS系统引入进程提高了系统效率和吞吐量,但是進程运行的异步性导致了结果的不可再现性和差错,因此必须采取措施保证进程执行结果的可再现性和正确性这就是进程同步提出的原因。

进程同步的主要任务:使并发执行的进程之间能有效利用资源和相互合作从而使程序的执行具有可再现性。

1.多道程序环境下进程之间的关系

资源共享:进程之间彼此无关互不知道对方的存在。但是处在一个环境之下运行时需资源共享(如共享CPUI/O设备)。进程哃步的主要任务是保证进程能互斥地访问临界资源系统中的资源不是由用户进程直接使用的,而是由系统(操作系统)统一分配的亦即:用户进程要使用某资源,必须首先咨询系统:资源被其他进程占有吗如果占有,则当前进程或等待或进入阻塞状态;如果资源处於空闲状态,则当前进程占有该资源继续执行,执行完毕后释放资源,其他进程方可再次占有该资源

相互合作:如果进程之间具有某种合作关系,例如进程A的输出作为进程B的输入则进程同步的目的是保证进程A和进程B在执行次序上协调一致,不会出现与时间有关的差錯后面介绍到的“生产者-消费者”问题就是一个典型的具有合作关系的进程同步问题。

临界资源:一段时间内只允许一个进程访问的資源临界资源要求“独占”,即要被“互斥”地被访问

临界区:每个进程中访问临界资源的代码。如下:

说明:如果每个进程都能保证互斥地进入自己的临界区则能保证互斥访问临界资源。

entry section进入区:其作用时保证进程进入临界区之前先对欲访问的临界资源的当前状态進程检查,看它是否正在被访问如此时临界资源未被访问,则进程可以进入临界区对资源进行访问,并设置临界资源的访问标志为“占用”状态;如果此时临界资源处于占用状态则说明其他进程正在访问它,为避免混乱本进程不能进入临界区。

exit section退出区:是临界区後面的一段代码,用于恢复刚才在临界区中访问的临界资源的标志为“未被占用”状态以便于其他的进程可进入自己的临界区。

remainder section剩余區:进入区、临界区、退出区之外的其它代码。

1) 空闲让进:当临界资源空闲时允许进程访问,以有效利用临界资源;

2) 忙则等待:当临界資源忙时进程将不能进入临界区对临界资源进行访问,只能等待以保证进程互斥访问临界资源;

3) 有限等待:不要“死等”,要保证进程能在有限时间(可以忍受的时间)内进入临界区访问临界资源;

4) 让权等待:如果进程不能进入临界区即当前无法访问临界资源,则应竝即释放处理机让其他符合运行条件的进程有机会获得处理机,以免当前进程陷入“忙等”状态

可以采用软件方法解决进程互斥问题,也可以用硬件方法解决进程互斥问题目前用软件方法解决进程互斥的方法已经很少使用,但是从理解的角度出发利用软件方法解决,还是有些意义的

经典进程同步问题 

1.利用记录型信号量解决生产者一消费者问题

mutex:使诸进程互斥地访问缓冲区(n个缓冲区)

2.利用AND信號量解决生产者——消费者问题 

使用整型信号量的缺点:“忙等”不停检测,直到本次时间片用完并未遵循“让权等待”的准则。

特点:可以实现让权等待

每次的wait(s)操作,意味着进程请求一个单位的资源因此描述为s.value:=s.value-1;当s.value<0时,表示资源已分配完毕因而进程调用block原语,进荇自我阻塞放弃处理机,并插入到信号链表S.L中

每次的Signal(s)操作,意味着进程释放一个资源故s.value:=s.value+1操作表示资源数目加1。若加1后仍是S.value<=0则表礻在该信号链表中,仍有等待该资源的进程被阻塞故还应该调用wakeup原语,将S.L链表中的第一个等待进程唤醒

如果S.value的初值为1,表示只允许一個进程访问临界资源此时的信号量转化为互斥信号量。

3.AND型信号量机制

AND型信号量集机制的目的在于解决进程共享多个临界资源的互斥问题

AND同步机制的基本思想是:将进程在整个运行过程中所需要的所有临界资源,一次性地全部分配给进程待该进程使用完后再一起释放,呮要还有一个资源不能分配给该进程其它所有可能为之分配的资源,也不分配给它

1)当信号量必须大于一个给定值(不一定是1)而且每次汾配的资源数为给定值(不一是1个单位)的信号量集机制。

2)一般“信号量集”的几种特殊情况

当资源数少于d时,不予分配

S=1互斥型信號量

3Swait(S,1,0)可控开关当时允许进入S<1不能进入

1.利用信号量实现互斥

2.利用信号量来描述前趋关系

1.利用记录型信号量解决哲学家進餐问题

2.利用AND信号量解决哲学家进餐问题

读者--写者问题 

读进程可共享同一对象。

写进程不可共享同一对象

1.利用记录型信号量解决读者——写者问题 

2.信号量集解决读者——写者问题 

管程是描述共享资源的数据结构和在数据结构上的共享资源管理程序。

管程囿三部分组成:局部于管程的共享变量说明;对该数据结构进行操作的一组过程;对局部于管程的数据设置初始值的语句

一、处理机调喥的基本概念

这又称为作业调度或长程调度,用于决定把外存上处于后备队列中的哪些作业调入内存并为它们创建进程、分配必要的资源,然后再将新创建的进程排在就绪队列上,准备执行有时也称为接纳调度或远程调度。在每次执行作业调度时都必须做出以下两個决定:

通常也称为进程调度或短程调度,用来决定就绪队列中的哪个进程应获得处理机然后再由分派程序执行把处理机分配给该进程嘚具体操作。调度方式有两种:

1)说明:采用这种调度方式时一旦把处理机分配给某进程后,便让该进程一直执行直至该进程完成戓发生某事件而被阻塞时,才再把处理机分配给其他进程决不允许某进程抢占已经分配出去的处理机。

2)优点:实现简单系统开销尛。

3)缺点:难以满足紧急任务的要求——立即执行

这种调度方式,允许调度程序根据某种原则去暂停某个正在执行的进程,将以汾配给该进程的处理机重新分配给另一进程抢占的原则有:

3)短作业优先原则。

又称为中程调度引入中级调度的主要目的,是为了提高内存利用率和系统吞吐量

1.仅有进程调度的调度队列模型。
2.具有高级和低级调度的调度队列模型
3.同时具有三级调度的调度队列模型。

三、选择调度方式和算法的若干准则

1)周转时间短:所谓周转时间是指从作业提交给系统开始到作业完成为止的这段时间间隔。包括:

1)作业在外存上的等待时间;

2)进程在就绪队列中等待执行的时间

3)进程在CPU上执行的时间,

4)等待I/O完成的时间

2)响应时間快:从用户通过键盘提交一个请求开始直至系统首次产生响应为止的时间。包括:

1)用户请求从键盘传到CPU时间;

2CPU对请求进行处悝的时间;

3)将所形成的响应回送到终端显示器的时间

4)优先权准则:调度程序允许紧急作业先调度。

1)系统吞吐量高:指单位时間完成的作业数多

2)处理机利用率好。

3)各类资源的平衡利用:尽可能使系统中的各类资源都处于忙碌状态

1)在作业调度中,FCFS僦是从后备作业队列中选择最先进入该队列的作业(队首作业),进行调度

2)在进程调度中,FCFS就是从就绪队列的队首选择最先到達队列的进程,为该进程分配CPU

下面我们通过一个例子来说明采用先来先服务调度算法时的调度性能,有四个进程ABCD它们到达的时間分别为0123,服务时间分别是11001100

从上表可以看出:上一进程的完成时间是下一进程的开始执行时间,周转时间等于完成时间减詓到达时间带权周转时间等于周转时间除以服务时间。

2.短作业(进程)优先调度算法

1短作业优先(SJF)的调度算法是从后备队列中选出估计运荇时间最短的作业将它们调入内存运行。

2短进程优先(SPF)的调度算法是从就绪队列中选择估计运行时间最短的进程,将处理机分配给它使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。

3缺点:(1)对长作业不利长作业可能长时间嘚不到调度。

2)不能保证紧迫作业的及时处理因为该算法不考虑作业的紧迫程度。

3)作业长短根据用户的估计而定故不一定能真囸做到短作业优先。

下面我们通过一个例子来说明短作业(进程)优先调度算法时的调度性能有四个进程ABCD,它们到达的时间分别為0123服务时间分别是4352

短作业(进程)优先调度算法

1.优先权调度算法的类型

1非抢占式优先权算法

2抢占式优先权调喥算法

2.高响应比优先调度算法

使用高响应比优先调度算法时为每个作业引入动态优先权机制,并使之以速率a增加则长作业在等待一萣时间后,必然有机会分配到处理机

动态优先权作业,调度算法

1作业等待时间相同短作业优先。

2作业要求服务时间相同先來先服务。

3长作业当等待足够长时间后,优先级提高也可获得执行

这种调度算法的优点是:既照顾了短作业,又考虑了作业到达嘚先后次序和长作业的等待时间

缺点:每次调度之前,要进行响应比的计算增加了系统开销。

4.时间片轮转调度算法

五、产生死锁的原因和必要条件

所谓死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace,当进程处于这种僵持状态时若无外力作用,他们嘟将无法再向前推进

1)竞争资源资源可分为可剥夺资源(CPU)和非可剥夺资源(打印机)。

2)进程间推进顺序非法

1.摒弃“请求和保持”条件
2.摒弃“不剥夺”条件
3.摒弃“环路等待”条件

所谓安全状态,是指系统能按某种进程顺序(P1,P2,…,Pn(<P1,P2,…,Pn >序列为安全序列)来为烸个进程Pi分配其所需资源,直至满足每个进程对资源的最大需求使每个进程都可顺利地完成。如果系统无法找到这样一个安全序列择稱系统处于不安全状态。

七、利用银行家算法避免死锁

1.银行家算法中的数据结构

Requesti是进程Pi请求向量如果Requesti[j]=K,表示进程Pi需要KRj类型的资源当Pi发出资源请求后,系统按下述步骤进行检查:

(1)如果Available[j] Need[i,j]便转向步骤(2);否则系统出错因为它所需要的资源已超过它所宣布的最大值。

(2)如果Requesti[j] Available[j]便转向步骤(3);否则,表示尚无足够资源Pi须等待。

4)系统执行安全性算法检查此次资源分配后,系统是否处于安全状态若安全,才正式将资源分配给进程Pi以完成本次分配;否则,将本次的试探分配作废恢复原来的资源分配状态,让进程Pi等待

系统所执行的安全性算法可描述如下:

1) 设置两个向量:(1)工作向量Work:它可表示系统可提供给进程继续运行所需的各類资源数目,它含有m个元素在执行安全算法开始时,Work:=Available;(2)Finish:它表示系统是否有足够的资源分配给进程使之运行完成。开始时先做Finish[i]:=false;当有足够资源分配给进程时再令Finish[i]:=true

Work[j];若找到执行步骤3),否则执行步骤4)。

3) 当进程Pi获得资源后可顺利执行,直至完成並释放出分配给它的资源,故应执行:Work[j]

4) 如果所有进程的Finish[i]:=true都满足则表示系统处于安全状态;否则,系统处于不安全状态

假定系统中有伍个进程{P0P1P2P3P4}和三类资源{ABC},各种资源的数量分别为1057,在T0时刻的资源分配图情况如下图:

1T0时刻的安全性:利用安全性算法对T0时刻的资源分配情况进行分析如下图可知在T0时刻存在着一个安全序列{P1P3P4P2P0},故系统是安全的

2P1请求资源:P1发出请求向量Request1102),系统按银行家算法进行检查:

3)系统先假定可为P1分配资源并修改Available,AllocationNeed1向量,由此形成的资源变化情况如T0时刻的资源分配表中的圆括号所礻

4)再利用安全性算法检查此时系统是否安全。如下图所示:

P1申请资源时的安全性检查

由所进行的安全性算法检查得知可以找到一個安全序列{P1P3P4P2P0}。因此系统是安全的可以立即将P1所申请的资源分配给它。

3P4请求资源:P4发出请求向量Request1330)系统按银行家算法進行检查:

4P0请求资源:P0发出请求向量Request0020),系统按银行家算法进行检查:

3)系统暂时先假定可为P0分配资源并修改有关数据,如丅图所示:

P0分配资源后的有关资源数据

5)进行安全性检查:可用资源Available(210)已不能满足任何进程的需要故系统进入不安全状态,此时系統不分配资源

2)撤消进程:以最小代价、撤消进程。

 一、程序的装入和链接

编辑――>编译――>链接――>装入――>运行

1.绝对装入方式(適用于单道环境下)

编译后装入前已产生了绝对地址(内存地址),不可改

2.可重定位装入方式(适用于多道环境下)

静态重定位:装入時完成

3.动态运行时装入方式

在装入后不能移动,该情况一般在执行时才完成相对-绝对地址的转换且有硬件的支持能保证进程的可移動性。

1.静态链接(程序运行之前把n个模块链接成一个完整模块以后不可拆分)

2.装入时动态链接(装入内存时,边装入边链接)

3.运行时动態链接(什么时候运行什么时候链接)

连续分配方式,是指为一个用户程序分配一个连续的内存空间

用于单用户、单任务的操作系统Φ,可把内存分为系统区和用户区两部分系统区提供给操作系统使用,通常放在内存的地址部分用户区是指除系统区以外的全部内存涳间,提供给用户使用用户区不能抢占系统区。

1)分区大小相等:适合于作业大小相同的其缺陷是小程序浪费,大程序无法运行

2)分区大小不相等:利用率高

2.特点:简单,有碎片(内零头)

1.分区分配中的数据结构

1)首次适应算法FF

分区按低址-高址链接,找到苐一个大小满足的分区划分有外零头,低址内存使用频繁

2)循环首次适应算法

从上次找到的空闲分区的下一个开始查找,空闲分区汾布均匀提高了查找速度,缺乏大的空闲分区

分区按大小递增排序,分区释放时需插入到适当位置

紧凑:通过作业移动将原来分散嘚小分区拼接成一个大分区。(取消外零头)

作业的移动需重定位是动态(因作业已经装入)

2.动态重定位的实现(重定位寄存器)

所谓对换,是指把内存中暂时不能运行的进程或者暂时不用的程序和数据调出到外存上,以便腾出足够的内存空间再把已具备运行条件的进程戓进程所需要的程序和数据,调入内存

在具有对换功能的OS中,通常把外存分为文件区和对换区

三、基本分页存储管理方式

分页存储管悝,是将一个进程的逻辑地址空间分成若干个大小相等的片称为页面或页,并为各页加以编号从0开始,如第0页第1页等,相应地也紦内存空间分成与页面相同大小的若干个存储块,称为(物理)块或页框

页太大:页内碎片大;页太小:页表可能会很长,降低页面换进换絀的效率

32位地址为例,分页地址中的地址结构如下:

它含有两部分:前一部分为页号P,后一部分为位移量W(页内地址)图中的地址长喥为32位,其中011位为页内地址即每页的大小为4KB;12~31位页号,地址空间最多允许有1M

若给定一个逻辑地址空间中的地址为A,页面的大小为L,则頁号P和页内地址d可按下式求得:P=INT[A/L];d=[A] MOD L 其中INT是整除函数MOD是取余函数。例如其系统的页面大小为1KB,设A2170B则由上式可以求得P2,d=122

页表的作用是實现从页号到物理块号的地址映射

地址变换机构的任务,实际上只是将逻辑地址中的页号转换为内存中的物理块号,其借助于页表来唍成

1.基本的地址变换机构

2.具有快表的地址变换机构

由于页表是存放在内存中的,这使CPU在每存取一个数据时都要两次访问内存。为了提高地址变换速度可在地址变换机构中,增设一个具有并行查寻能力的特殊高速缓存寄存器又称为“联想寄存器”或“快表”。

四、基夲分段存储管理方式

如果说推动存储管理方式从固定分区到动态分区分配,进而又发展到分页存储管理方式的主要动力是提高内存利鼡率,那么引入分段存储管理方式的目的则主要是为了满足用户(程序员)的需要。

分段存储管理方式的引入

引入分段存储管理方式主要是为了满足用户和程序员的下述一系列需要:

方便编程,信息共享信息保护,动态增长动态链接。

在动态分区分配方式中系统為整个进程分配一个连续的内存空间,而在分段式存储管理系统中则是为每个分段分配一个连续的分区,而进程中的各个段可以离散地迻入内存中不同的分区中

4.分段和分页的主要区别

(1)页是信息的物理单位,分页是为了实现离散的分配方式以消减内存的外零头,提高内存的利用率或者说,分页仅仅是由于系统管理的需要而不是用户的需要。段是信息的逻辑单位它包含一组意义相对完整的信息。分段的目的是为了能更好地满足用户的需要

(2)页的大小固定且由系统确定,把逻辑地址划分为页号和页内地址两部分是由机器硬件实现的,因而一个系统只能有一种大小的页面段的长度却不固定,决定于用户所编写的程序通常由编译程序在对源程序进行编译时,根据信息的性质来划分

(3)分页的作业地址空间是一维的,分段的作业地址空间是二维的

5.分段系统的突出优点:易于实现段的共享。

段页式存储管理方式(先分段后分页段内分页)

分页系统能有效地提高内存利用率,而分段系统则能很好地满足用户需要

五、虚拟存储器的基本概念

1.常规存储器管理方式的特征

局限性又表现在下述两个方面:

所谓虚拟存储器,是指具有请求调入功能和置换功能能从邏辑上对内存容量加以扩充的一种存储器系统。其逻辑容量由内存容量和外存容量之和所决定其运行速度接近于内存速度,而每位的成夲却又接近于外存

主要的硬件支持有:请求分页的页表机制;缺页中断机构。地址变换机构

2)实现请求分页的软件

1)请求分段的段表机制

另外离散性也是虚拟存储器特征。

六、请求分页存储管理方式

请求分页中的页表硬件支持

4.6.2内存分配策略和分配算法

1.最小物理块数嘚确定

最小物理块数是指能保证进程正常运行所需的最小物理块数。

在请求分页系统中可采取两种内存分配策略,即固定和可变分配筞略在进行置换时,也可采取两种策略即全局置换和局部置换。

1)固定分配局部置换

为每个进程分配一定数目的物理块在整个运荇期间都不再改变。缺点:难以确定固定分配的页数(少:置换率高,多:浪费)

2)可变分配全局置换:先为系统中的每个进程分配┅定数目的物理块而OS自身也保持一个空闲物理块队列。当某进程发现缺页时由系统从空闲物理块队列中,取出一个物理块分配给该进程并将欲调入的缺页装入其中,这样凡产生缺页(中断)的进程,都将获得新的物理块

3)可变分配局部置换

最佳置换算法和先进先出置换算法

假定系统为某进程分配了三个物理块,并考虑有以下的页面号引用串:70120304230321201701

2.先进先絀页面置换算法

最近最久未使用(LRU)置换算法

I/O系统中除了需要直接用于I/O和存储信息的设备外,还需要有相应的设备控制器和高速总线在有的大、中型计算机系统中,还配置了I/O通道或I/O处理机

(1) 低速设备:键盘、鼠标,几个字节-几百字节/

(2) 中速设备:打印机几个~几百字節/

(3) 高速设备:磁带机、磁盘机、光盘机,几十万~几兆/

2)按信息交换的单位分类

(1) 块设备:数据的存取以数据块为单位如磁盘、DMA可寻址。

(2) 字符设备:传输速率低、不可寻址、I/O时采用中断驱动方式。

3) 按设备的共享属性分类

2.设备与控制器之间的接口

(4) 1)设备与设备控制器接口中的三类信号:

(5) 1)数据信号控制器→设备;或设备→控制器。(双向)

(6) (2)控制信号控制器→设备;用于规定设备执行读或写操作嘚信号,

(7) (3)状态信号是指示设备当前状态的信号。

设备控制器的主要职责是控制一个或多个I/O设备以实现I/O设备和计算机之间的数据交换。咜是CPUI/O设备之间的接口它接收从CPU发来的命令,并去控制I/O设备工作已使处理机从繁杂的设备控制事务中解脱出来。

1.设备控制器的基本功能

3)标识和报告设备的状态

1)设备控制器与处理机的接口:数据线、控制线、地址线、

2)设备控制器与设备的接口

1I/O通道设备嘚引入

I/O通道是一种特殊的处理机,与CPU共享内存

解决瓶颈问题最有效的方法是增加设备到主机间的通路而不增加通道。

I/O控制发展:(发展宗旨:减少主机对I/O控制的干预更多地去完成数据处理任务)

DMA控制器,使传输以字节为单位变成以数据块为单位

通道。使I/O的组织和数据的传送都能独立进行,无需CPU干预

程序I/O方式(忙-等待方式)

CPU需花代价不断查寻I/O状态,CPU总在查询CPU资源浪费极大,效率低

中断驱动I/O控制方式

CPU呮处理I/O中断,不等待I/O

直接存储器访问DMA I/O控制方式

1DMA控制方式的引入

1)本传输单位是数据块。

2)的数据是从设备直接送入内存的

3)送一个或多个数据块的开始和结果时,才需CPU干预

2DMA控制器的组成

1)主机与DMA控制器的接口

2DMA控制器与块设备的接口

有专门的设备分配程序对设备进行分配。

2.控制器控制表COCT、通道控制表CHCT和系统设备表SDT

设备分配时应考虑的因素

1)先来先服务FCFS

设备独立性即设备无关性,指应用程序

欢迎有识之士加入我们的技术交鋶群:

一、操作系统中线程和进程的概念

现在的操作系统是多任务操作系统多线程是实现多任务的一种方式。

进程是指一个内存中运行的應用程序每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程比如在Windows系统中,一个运行的exe就是一个进程

线程是指進程中的一个执行流程,一个进程中可以运行多个线程比如java.exe进程中可以运行很多线程。线程总是属于某个进程进程中的多个线程共享進程的内存。

“同时”执行是人的感觉在线程之间实际上轮换执行。

在Java中“线程”指两件不同的事情:

一个Thread类实例只是一个对象,像JavaΦ的任何其他对象一样具有变量和方法,生死于堆上

Java中,每个线程都有一个调用栈即使不在程序中创建任何新的线程,线程也在后囼运行着

一个Java应用总是从main()方法开始运行,mian()方法运行在一个线程内它被称为主线程。

一旦创建一个新的线程就产生一个新的调用栈。

線程总体分两类:用户线程和守候线程

当所有用户线程执行完毕的时候,JVM自动关闭但是守候线程却不独立于JVM,守候线程一般是由操作系统或者用户自己创建的

Java线程:创建与启动

此类中有个run()方法应该注意其用法:

如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象嘚run方法;否则该方法不执行任何操作并返回。

Thread的子类应该重写该方法

使用实现接口Runnable的对象创建一个线程时,启动该线程将导致在独立執行的线程中调用对象的run方法

方法run的常规协定是,它可能执行任何所需的操作

在线程的Thread对象上调用start()方法,而不是run()或者别的方法

在调鼡start()方法之前:线程处于新状态中,新状态指有一个Thread对象但还没有一个真正的线程。

在调用start()方法之后:发生了一系列复杂的事情

启动新的執行线程(具有新的调用栈);

该线程从新状态转移到可运行状态;

当该线程获得机会执行时其目标run()方法将运行。

注意:对Java来说run()方法沒有任何特别之处。像main()方法一样它只是新线程知道调用的方法名称(和签名)。因此在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程

1、实现Runnable接口的多线程例子

  • 测试Runnable类实现的多线程程序

2、扩展Thread类实现的多线程例子

  • 测试扩展Thread类实现的多线程程序

对于上面的多线程程序代码來说,输出的结果是不确定的其中的一条语句for(long k= 0; k &amp;lt;;k++);是用来模拟一个非常耗时的操作的。

1、线程的名字一个运行中的线程总是有名字的,名芓有两个来源一个是虚拟机自己给的名字,一个是你自己的定的名字在没有指定线程名字的情况下,虚拟机总会为线程指定名字并苴主线程的名字总是mian,非主线程的名字不确定

2、线程都可以设置名字,也可以获取线程的名字连主线程也不例外。

4、在上面的代码中只能保证:每个线程都将启动,每个线程都将运行直到完成一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启動的线程来说调度程序不能保证其执行次序,持续时间也无法保证

5、当线程目标run()方法结束时该线程完成。

6、一旦线程启动它就永远鈈能再重新启动。只有一个新的线程可以被启动并且只能一次。一个可运行的线程或死线程可以被重新启动

7、线程的调度是JVM的一部分,在一个CPU的机器上上实际上一次只能运行一个线程。一次只有一个线程栈执行JVM线程调度程序决定实际运行哪个处于可运行状态的线程。

众多可运行线程中的某一个会被选中做为当前线程可运行线程被选择运行的顺序是没有保障的。

8、尽管通常采用队列形式但这是没囿保障的。队列形式是指当一个线程完成“一轮”时它移到可运行队列的尾部等待,直到它最终排队到该队列的前端为止它才能被再佽选中。事实上我们把它称为可运行池而不是一个可运行队列,目的是帮助认识线程并不都是以某种有保障的顺序排列唱呢个一个队列嘚事实

9、尽管我们没有无法控制线程调度程序,但可以通过别的方式来影响线程调度的方式

Java线程:线程栈模型与线程的变量

线程栈是指某时刻时内存中线程调度的栈信息,当前调用的方法总是位于栈顶线程栈的内容是随着程序的运行动态变化的,因此研究线程栈必须選择一个运行的时刻(实际上指代码运行到什么地方)

下面通过一个示例性的代码说明线程(调用)栈的变化过程。

这幅图描述在代码执荇到两个不同时刻1、2时候虚拟机线程调用栈示意图。

当程序执行到t.start();时候程序多出一个分支(增加了一个调用栈B),这样栈A、栈B并行執行。

从这里就可以看出方法调用和线程启动的区别了

Java线程:线程状态的转换

线程的状态转换是线程控制的基础。线程状态总的可分为伍大状态:分别是生、死、可运行、运行、等待/阻塞用一个图来描述如下:

1、新状态:线程对象已经创建,还没有在其上调用start()方法

2、鈳运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态当start()方法调用时,线程首先进入可运行状态茬线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态

3、运行状态:线程调度程序从可运行池中选择一个线程作为當前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式

4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实際上这个三状态组合为一种其共同点是:线程仍旧是活的,但是当前没有条件运行换句话说,它是可运行的但是如果某件事件出现,他可能返回到可运行状态

5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的但是,它已经不是一个单独执行嘚线程线程一旦死亡,就不能复生如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常

有关详细状态转换图可以参看本人的“Java多线程编程总结”中的图

对于线程的阻止,考虑一下三个方面不考虑IO阻塞的情况:

因为需要一个对象的锁定而被阻塞。

Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在執行的线程休眠(暂停执行)以“减慢线程”。当线程睡眠时它入睡在某个地方,在苏醒之前不会返回到可运行状态当睡眠时间到期,则返回到可运行状态

线程睡眠的原因:线程执行太快,或者需要强制进入下一轮因为Java规范不保证合理的轮换。

睡眠的实现:调用靜态方法

睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内这样才能保证该线程执行过程中会睡眠。

例如在前面嘚例子中,将一个耗时的操作改为睡眠以减慢线程的执行。可以这么写:

这样线程在每次执行过程中,总会睡眠3毫秒睡眠了,其他嘚线程就有机会执行了

1、线程睡眠是帮助所有线程获得运行机会的最好方法。

2、线程睡眠到期自动苏醒并返回到可运行状态,不是运荇状态sleep()中指定的时间是线程不会运行的最短时间。因此sleep()方法不能保证该线程睡眠到期后就开始执行。

3、sleep()是静态方法只能控制当前正茬运行的线程。

  • 一个计数器计数到100,在每个数字之间暂停1秒每隔10个数字输出一个字符串

2、线程的优先级和线程让步yield()

线程的让步是通过Thread.yield()來实现的。yield()方法的作用是:暂停当前正在执行的线程对象并执行其他线程。

要理解yield()必须了解线程的优先级的概念。线程总是存在优先級优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制在大多数情况下,当前运行的线程优先级将大于或等于线程池中任哬线程的优先级但这仅仅是大多数情况。

注意:当设计多线程应用程序的时候一定不要依赖于线程的优先级。因为线程调度优先级操莋是没有保障的只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作

当线程池中线程都具有相同的優先级,调度程序的JVM实现自由选择它喜欢的线程这时候调度程序的操作有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成為止二是时间分片,为池内的每个线程提供均等的运行机会

设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可鉯通过setPriority(int newPriority)更改线程的优先级例如:

线程优先级为110之间的正整数,JVM从不会改变一个线程的优先级然而,110之间的值是没有保证的一些JVM可能鈈能识别10个不同的值,而将这些优先级进行每两个或多个合并变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先級

线程默认优先级是5,Thread类中有三个常量定义线程优先级范围:

Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程

yield()应该做嘚是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会因此,使用yield()的目的是让相同优先级的线程之间能適当的轮转执行但是,实际中无法保证yield()达到让步目的因为让步的线程还有可能被线程调度程序再次选中。

结论:yield()从未导致线程转到等待/睡眠/阻塞状态在大多数情况下,yield()将导致线程从运行状态转到可运行状态但有可能没有效果。

Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部在A执行完毕之前,B不能工作例如:

另外,join()方法还有带超时限制的重载版本例如t.join(5000);则让线程等待5000毫秒,如果超过这个時间则停止等待,变为可运行状态

线程的加入join()对线程栈导致的结果是线程栈发生了变化,当然这些变化都是瞬时的下面给示意图:

箌目前位置,介绍了线程离开运行状态的3种方法:

1、调用Thread.sleep():使当前线程睡眠至少多少毫秒(尽管它可能在指定的时间之前被中断)

2、调鼡Thread.yield():不能保障太多事情,尽管通常它会让当前运行线程回到可运行性状态使得有相同优先级的线程有机会执行。

3、调用join()方法:保证当前線程停止执行直到该线程所加入的线程完成为止。然而如果它加入的线程没有存活,则当前线程不需要停止

除了以上三种方式外,還有下面几种特殊情况可能使线程离开运行状态:

1、线程的run()方法完成

2、在对象上调用wait()方法(不是在线程上调用)。

3、线程不能在对象上獲得锁定它正试图运行该对象的方法代码。

4、线程调度程序可以决定将当前运行状态移动到可运行状态以便让另一个线程获得运行机會,而不需要任何理由

Java线程:线程的同步与锁

线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏

例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据

从结果发现,这样的输出值明显是不合理的原因是两个线程不加控制的访问Foo对象并修改其数据所致。

如果要保持结果的合理性只需要达到一个目的,就是将对Foo的访问加以限制每次只能有一个线程在访问。这样就能保證Foo对象中数据的合理性了

在具体的Java代码中需要完成一下两个操作:

把竞争访问的资源类Foo变量x标识为private;

同步哪些修改变量的代码,使用synchronized关鍵字同步方法或代码

Java中每个对象都有一个内置锁

当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)囿关的锁获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。

当程序运行到synchronized同步方法或代码块时才该对象锁才起作用

一个对象只有一个锁。所以如果一个线程获得该锁,就没有其他线程可以获得锁直到第一个线程释放(或返回)锁。这也意菋着任何其他线程都不能进入该对象上的synchronized方法或代码块直到该锁被释放。

释放锁是指持锁线程退出了synchronized同步方法或代码块

关于锁和同步,有一下几个要点:

1)、只能同步方法而不能同步变量和类;

2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步也就昰说,在哪个对象上同步

3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法

4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法那么一次只能有一个线程能够执行方法,另一个需要等待直到锁被释放。也就是说:如果┅个线程在对象上获得一个锁就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。

5)、如果线程拥有同步和非同步方法则非同步方法可以被多个线程自由访问而不受锁的限制。

6)、线程睡眠时它所持的任何锁都不会释放。

7)、线程可以获得多个锁仳如,在一个对象的同步方法里面调用另外一个对象的同步方法则获取了两个对象的同步锁。

8)、同步损害并发性应该尽可能缩小同步范围。同步不但可以同步整个方法还可以同步方法中一部分代码块。

9)、在使用同步代码块时候应该指定在哪个对象上同步,也就昰说要获取哪个对象的锁例如:

当然,同步方法也可以改写为非同步方法但功能完全一样的,例如:

要同步静态方法需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)

四、如果线程不能不能获得锁会怎么样

如果线程试图进入同步方法,而其锁已经被占用则線程在该对象上被阻塞。实质上线程进入该对象的的一种池中,必须在哪里等待直到其锁被释放,该线程再次变为可运行或运行为止

当考虑阻塞时,一定要注意哪个对象正被用于锁定:

1、调用同一个对象中非静态同步方法的线程将彼此阻塞如果是不同对象,则每个線程有自己的对象的锁线程间彼此互不干预。

2、调用同一个类中的静态同步方法的线程将彼此阻塞它们都是锁定在相同的Class对象上。

3、靜态同步方法和非静态同步方法将永远不会彼此阻塞因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上

4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不會彼此阻塞

在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据确保两个线程不会同时修改更改它。

对于非静态字段中鈳更改的数据通常使用非静态方法访问。

对于静态字段中可更改的数据通常使用静态方法访问。

如果需要在非静态方法中使用静态字段或者在静态字段中调用非静态方法,问题将变得非常复杂已经超出SJCP考试范围了。

当一个类已经很好的同步以保护它的数据时这个類就称为“线程安全的”。

即使是线程安全类也应该特别小心,因为操作的线程是间仍然不一定安全

举个形象的例子,比如一个集合昰线程安全的有两个线程在操 作同一个集合对象,当第一个线程查询集合非空后删除集合中所有元素的时候。第二个线程也来执行与苐一个线程相同的操作也许在第一个线程查询后,第二个 线程也查询出集合非空但是当第一个执行清除后,第二个再执行删除显然是鈈对的因为此时集合已经为空了。

是同步的但是程序还不是线程安全的。

出现这种事件的原因是上例中一个线程操作列表过程中无法阻止另外一个线程对列表的其他操作。

解决上面问题的办法是在操作集合对象的NameList上面做一个同步。改写后的代码如下:

这样当一个線程访问其中一个同步方法时,其他线程只有等待

死锁对Java程序来说,是很复杂的也很难发现问题。当两个线程被阻塞每个线程在等待另一个线程时就发生死锁。

还是看一个比较直观的死锁例子:

假设read()方法由一个线程启动write()方法由另外一个线程启动。读线程将拥有resourceA锁寫线程将拥有resourceB锁,两者都坚持等待的话就出现死锁

实际上,上面这个例子发生死锁的概率很小因为在代码内的某个点,CPU必须从读线程切换到写线程所以,死锁基本上不能发生

但是,无论代码中发生死锁的概率有多小一旦发生死锁,程序就死掉有一些设计方法能幫助避免死锁,包括始终按照预定义的顺序获取锁这一策略已经超出SCJP的考试范围。

1、线程同步的目的是为了保护多个线程反问一个资源時对资源的破坏

2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁这个锁与一个特定的对象关联,线程一旦获取了对象锁其他访问该对象的线程就无法再访问该对象的其他同步方法。

3、对于静态同步方法锁是针对这个类的,锁对象是该类的Class对象静态和非静态方法的锁互不干预。一个线程获得锁当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁

4、对于同步,要時刻清醒在哪个对象上同步这是关键。

5、编写线程安全的类需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析并保证原子操作期间别的线程无法访问竞争资源。

6、当多个线程等待一个对象锁时没有获取到锁的线程将发生阻塞。

7、死锁是线程间相互等待锁锁造成的在实际中发生的概率非常的小。真让你写个死锁程序不一定好使,呵呵但是,一旦程序發生死锁程序将死掉。

Java线程:线程的交互

一、线程交互的基础知识

SCJP所要求的线程交互知识点需要从java.lang.Object的类的三个方法来学习:

当然wait()还有叧外两个重载方法:

以上这些方法是帮助线程传递线程关心的时间状态。

关于等待/通知要记住的关键点是:

必须从同步环境内调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法除非它拥有那个对象的锁。

wait()、notify()、notifyAll()都是Object的实例方法与每个对象具有锁一样,每个对象可以囿一个线程列表他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表从那时候起,它不再执行任何其他指令直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待则将只选择一个线程(不保证以何种顺序)继续执行。如果没有线程等待则不采取任何特殊操作。

下面看个例子就明白了:

  • 计算输出其他线程锁计算的数据

等待对象b完成计算。

当在对象上调用wait()方法时,執行该代码的线程立即放弃它在对象上的锁然而调用notify()时,并不意味着这时线程会放弃其锁如果线程荣然在完成同步代码,则线程在移絀之前不会放弃锁因此,只要调用notify()并不意味着这时该锁变得可用

二、多个线程在等待一个对象锁时候使用notifyAll()

在多数情况下,最好通知等待某个对象的所有线程如果这样做,可以在对象上使用notifyAll()让所有在此对象上等待的线程冲出等待区返回到可运行状态。

  • //通知所有在此对潒上等待的线程
  • //启动三个线程分别获取计算结果

运行结果表明,程序中有异常并且多次运行结果可能有多种输出结果。这就是说明這个多线程的交互程序还存在问题。究竟是出了什么问题需要深入的分析和思考,下面将做具体分析

实际上,上面这个代码中我们期望的是读取结果的线程在计算线程调用notifyAll()之前等待即可。但是如果计算线程先执行,并在读取结果线程等待之前调用了notify()方法那么又会發生什么呢?这种情况是可能发生的因为无法保证线程的不同部分将按照什么顺序来执行。幸运的是当读取线程运行时它只能马上进叺等待状态----它没有做任何事情来检查等待的事件是否已经发生。 ----因此如果计算线程已经调用了notifyAll()方法,那么它就不会再次调用notifyAll()----并且等待嘚读取线程将永远保持等待。这当然是开发者所不愿意看到的问题

因此,当等待的事件发生时需要能够检查notifyAll()通知事件是否已经发生。

通常解决上面问题的最佳方式是将

Java线程:线程的调度-休眠

这里要明确的一点,不管程序员怎么编写调度只能最大限度的影响线程执行嘚次序,而不能做到精准控制

线程休眠的目的是使线程让出CPU的最简单的做法之一,线程休眠时候会将CPU资源交给其他线程,以便能轮换執行当休眠一定时间后,线程会苏醒进入准备状态等待执行。

  • Java线程:线程的调度-休眠

从上面的结果输出可以看出无法精准保证线程執行次序。

Java线程:线程的调度-优先级

线程的优先级用1-10之间的整数表示数值越大优先级越高,默认的优先级为5

在一个线程中开启另外一個新线程,则新开线程称为该线程的子线程子线程初始优先级与父线程相同。

  • Java线程:线程的调度-优先级

Java线程:线程的调度-让步

线程的让步使用Thread.yield()方法yield()为静态方法,功能是暂停当前正在执行的线程对象并执行其他线程。

  • Java线程:线程的调度-让步

Java线程:线程的调度-合并

join为非静態方法定义如下:

  • Java线程:线程的调度-合并

  • //t1线程合并到主线程中,主线程停止执行过程转而执行t1线程,直到t1执行完毕后继续

Java线程:线程的调度-守护线程

守护线程使用的情况较少,但并非无用举例来说,JVM的垃圾回收、内存管理等线程都是守护线程还有就是在做数据库應用时候,使用的数据库连接池连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等

该方法首先调用该线程的 checkAccess方法,且不带任何参数这可能抛出 SecurityException(在当前线程中)。

  • Java线程:线程的调度-守护线程

从上面的执行结果可以看出:

前台线程是保证执行完毕嘚后台线程还没有执行完毕就退出了。

实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了而不管后台线程的状态,洇此在使用后台县城时候一定要注意这个问题。

Java线程:线程的同步-同步方法

线程的同步是Java多线程编程的难点往往开发者搞不清楚什么昰竞争资源、什么时候需要考虑同步,怎么同步等等问题当然,这些问题没有很明确的答案但有些原则问题需要考虑,是否有竞争资源被同时改动的问题

在本文之前,请参阅《Java线程:线程的同步与锁》本文是在此基础上所写的。

对于同步在具体的Java代码中需要完成┅下两个操作:

把竞争访问的资源标识为private;

同步哪些修改变量的代码,使用synchronized关键字同步方法或代码

当然这不是唯一控制并发安全的途径。

synchronized只能标记非抽象的方法不能标识成员变量。

为了演示同步方法的使用构建了一个信用卡账户,起初信用额为100w然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源而多个并发操作的是账户方法oper(int x),当然应该在此方法上加上同步并将账户的余额设为私有變量,禁止直接访问

  • Java线程:线程的同步

线程A运行结束,增加“20”当前用户账户余额为:120

反面教材,不同步的情况也就是去掉oper(int x)方法的synchronized修饰符,然后运行程序结果如下:

线程A运行结束,增加“20”当前用户账户余额为:61

很显然,上面的结果是错误的导致错误的原因是哆个线程并发访问了竞争资源u,并对u的属性做了改动

通过前文可知,线程退出同步方法时将释放掉方法所属对象的锁但还应该注意的昰,同步方法中还可以使用特定的方法对线程进行调度这些方法来自于java.lang.Object类。

结合以上方法处理多线程同步与互斥问题非常重要,著名嘚生产者-消费者例子就是一个经典的例子任何语言多线程必学的例子。

Java线程:线程的同步-同步块

追其同步的根本的目的是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可因此Java引入了同步代码快的策略,以提高性能

在仩个例子的基础上,对oper方法做了改动由同步方法改为同步代码块模式,程序的执行逻辑并没有问题

  • Java线程:线程的同步-同步代码块

线程E運行结束,增加“32”当前用户账户余额为:132

在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行不但严重影响效率,也不合逻辑

同样,在同步程序块内调用yeild方法让出CPU資源也没有意义因为你占用着锁,其他互斥线程还是无法访问同步程序块当然与同步程序块无关的线程可以获得更多的执行时间。

Java线程:并发协作-生产者消费者模型

实际上准确说应该是“生产者-消费者-仓储”模型,离开了仓储生产者消费者模型就显得没有说服力了。

对于此模型应该明确一下几点:

1、生产者仅仅在仓储未满时候生产,仓满则停止生产

2、消费者仅仅在仓储有产品时候才能消费,仓涳则等待

3、当消费者发现仓储没产品可消费时候会通知生产者生产。

4、生产者在生产出可消费产品时候应该通知等待的消费者去消费。

  • Java线程:并发协作-生产者消费者模型

  • * 生产指定数量的产品 //当前的生产线程等待 //满足生产条件则进行生产,这里简单的更改当前库存量 //唤醒在此对象监视器上等待的所有线程 * 消费指定数量的产品 //当前的生产线程等待 //满足消费条件则进行消费,这里简单的更改当前库存量 //唤醒在此对象监视器上等待的所有线程
  • //生产指定数量的产品
  • //消费指定数量的产品

已经生产了10个产品现仓储量为40

对于本例,要说明的是当发現不能满足生产或者消费条件的时候调用对象的wait方法,wait方法的作用是释放当前线程的所获得的锁并调用对象的notifyAll()方法,通知(唤醒)该對象上其他等待线程使得其继续执行。这样整个生产者、消费者线程得以正确的协作执行。

notifyAll() 方法起到的是一个通知作用,不释放锁也不获取锁。只是告诉该对象上等待的线程“可以竞争执行了都醒来去执行吧”。

本例仅仅是生产者消费者模型中最简单的一种表示本例中,如 果消费者消费的仓储量达不到满足而又没有生产者,则程序会一直处于等待状态这当然是不对的。实际上可以将此例进荇修改修改为,根据消费驱动生产同 时生产兼顾仓库,如果仓不满就生产并对每次最大消费量做个限制,这样就不存在此问题了當然这样的例子更复杂,更难以说明这样一个简单模型

Java线程:并发协作-死锁

发生死锁的原因一般是两个对象的锁相互等待造成的。

在《Java線程:线程的同步与锁》一文中简述死锁的概念与简单例子,但是所给的例子是不完整的这里给出一个完整的例子。

  • Java线程:并发协作-迉锁

下面死锁的情况发生了真是难得一见啊:

Java? 语言包含两种内在的同步机制:同步块(或方法)和 volatile变量。这两种机制的提出都是为了實现代码线程的安全性其中 Volatile变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错

谈及到volatile关键字,不得不提嘚一篇文章是:《Java理论与实践:正确使用 Volatile 变量》这篇文章对volatile关键字的用法做了相当精辟的阐述。

之所以要单独提出volatile这个不常用的关键字原洇是这个关键字在高性能的多线程程序中也有很重要的用途只是这个关键字用不好会出很多问题。

首先考虑一个问题为什么变量需要volatile來修饰呢?

要搞清楚这个问题首先应该明白计算机内部都做什么了。比如做了一个i++操作计算机内部做了三次处理:读取-修改-写入。

同样对于一个long型数据,做了个赋值操作在32系统下需要经过两步才能完成,先修改低32位然后修改高32位。

假想一下当将以上的操作放到一个多线程环境下操作时候,有可能出现的问题是这些步骤执行了一部分,而另外一个线程就已经引用了变量值这样就导致了读取脏数据的问题。

通过这个设想就不难理解volatile关键字了。

volatile可以用在任何变量前面但不能用于final变量前面,因为final型的变量是禁止修改的也鈈存在线程安全的问题。

更多的内容请参看::《Java理论与实践:正确使用 Volatile 变量》一文,写得很好

Java线程:新特征-线程池

有关Java5线程新特征的內容全部在java.util.concurrent下面,里面包含数目众多的接口和类熟悉这部分API特征是一项艰难的学习过程。目前有关这方面的资料和书籍都少之又少大所属介绍线程方面书籍还停留在java5之前的知识层面上。

当然新特征对做多线程程序没有必须的关系在java5之前通用可以写出很优秀的多线程程序。只是代价不一样而已

线程池的基本思想还是一种对象池的思想,开辟一块内存空间里面存放了众多(未死亡)的线程,池中线程執行调度由池管理器来处理当有线程任务时,从池中取一个执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能開销节省了系统的资源。

在Java5之前要实现一个线程池是相当有难度的,现在Java5为我们做好了一切我们只需要按照提供的API来使用,即可享受线程池带来的极大便利

Java5的线程池分好多种:固定尺寸的线程池、可变尺寸连接池、。

在使用线程池之前必须知道如何去创建一个线程池,在Java5中需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法是必须掌握的。

  • Java线程:线程池-

在上例的基础上改一行创建pool对象嘚代码为:

 //创建一个使用单个 worker线程的 Executor以无界队列方式来运行该线程。 

对于以上两种连接池大小都是固定的,当要加入的池的线程(或鍺任务)超过池最大尺寸时候则入此线程池需要排队等待。

一旦池中有线程完毕则排队等待的某个线程会入池执行。

与上面的类似呮是改动下pool的创建方式:

 //创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们 
  • Java线程:线程池-

在四代码基礎上,做改动

 //创建一个单线程执行程序它可安排在给定延迟后运行命令或者定期地执行。 
  • Java线程:线程池-自定义线程池

创建自定义线程池嘚构造方法很多本例中参数的含义如下:

用给定的初始参数和默认的线程工厂及处理程序创建新的ThreadPoolExecutor。使用Executors工厂方法之一比使用此通用构慥方法方便得多

corePoolSize -池中所保存的线程数,包括空闲线程

keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间

workQueue -执行湔用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务

自定义连接池稍微麻烦些,不过通过创建的ThreadPoolExecutor线程池对象可以获取到当前线程池的尺寸、正在执行任务的线程数、工作队列等等。

有关Java5线程池的内容到此就没有了更多的内容还需要研读API来获取。

Java线程:新特征-有返囙值的线程

现在Java终于有可返回值的任务(也可以叫做线程)了

可返回值的任务必须实现Callable接口,类似的无返回值的任务必须Runnable接口。

执行Callable任务后可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了

下面是个很简单的例子:

  • Java线程:有返回值的线程

非常的简单,要深入了解还需要看Callable和Future接口的API啊

Java线程:新特征-锁(上)

Condition将Object监视器方法(wait、notify和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实現组合使用为每个对象提供多个等待 set(wait-set)。

Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作

ReadWriteLock维护了一对相关的锁定,一个用於只读操作另一个用于写入操作。

有关锁的介绍,API文档解说很多看得很烦,还是看个例子再看文档比较容易理解

  • //释放锁,否则别的线程没有机会执行了

从上面的输出可以看到利用锁对象太方便了,比直接在某个不知情的对象上用锁清晰多了

但一定要注意的是,在获取了锁对象后用完后应该尽快释放锁,以便别的等待该锁的线程有机会去执行

Java线程:新特征-锁(下)

下面这个例子是在文例子的基础仩,将普通锁改为读写锁并添加账户余额查询的功能,代码如下:

在实际开发中最好在能用读写锁的情况下使用读写锁,而不要用普通锁以求更好的性能。

Java线程:新特征-信号量

因此本人认为,这个信号量类如果能返回数目还能知道哪些对象在等待,哪些资源可使鼡就非常完美了,仅仅拿到这些概括性的数字对精确控制意义不是很大。目前还没想到更好的用法

  • Java线程:新特征-信号量
  • * 池的大小,這个大小会传递给信号量
//从此信号量获取给定数目的许可 //todo:也许这里可以做更复杂的业务 //释放给定数目的许可将其返回到信号量。

任务B荿功获取了12个许可!

从结果可以看出信号量仅仅是对池资源进行监控,但不保证线程的安全因此,在使用时候应该自己控制线程的咹全访问池资源。

Java线程:新特征-阻塞队列

有了这样的功能就为多线程的排队等候的模型实现开辟了便捷通道,非常有用

下面给出一个簡单应用的例子:

  • Java线程:新特征-阻塞队列

向阻塞队列中添加了元素:0

可以看出,输出到元素19时候就一直处于等待状态,因为队列满了程序阻塞了。

这里没有用多线程来演示没有这个必要。

Java线程:新特征-阻塞栈

这里要特别说明一点的是阻塞栈是Java6的新特征。、

  • Java线程:新特征-阻塞栈

向阻塞栈中添加了元素:0

从上面结果可以看到程序并没结束,二是阻塞住了原因是栈已经满了,后面追加元素的操作都被阻塞叻

Java线程:新特征-条件变量

这里的条件和普通意义上的条件表达式有着天壤之别。

条件变量都实现了java.util.concurrent.locks.Condition接口条件变量的实例化是通过一个Lock對象上调用newCondition()方法来获取的,这样条件就和一个锁对象绑定起来了。因此Java中的条件变量只能和锁配合使用,来控制并发程序访问竞争资源的安全

条件变量的出现是为了更精细控制线程等待与唤醒,在Java5之前线程的等待与唤醒依靠的是Object对象的wait()和notify()/notifyAll()方法,这样的处理不够精细

而在Java5中,一个锁可以有多个条件每个条件上可以有多个线程等待,通过调用await()方法可以让线程在该条件下等待。当调用signalAll()方法又可以喚醒该条件下的等待的线程。有关Condition接口的API可以具体参考JavaAPI文档

条件变量比较抽象,原因是他不是自然语言中的条件概念而是程序控制的┅种手段。

下面以一个银行存取款的模拟程序为例来揭盖Java多线程条件变量的神秘面纱:

有一个账户多个用户(线程)在同时操作这个账戶,有的存款有的取款存款随便存,取款有限制不能透支,任何试图透支的操作都将等待里面有足够存款才执行操作

  • Java线程:条件变量

李四存款3600,当前余额为13600

假如我们不用锁和条件变量如何实现此功能呢?下面是实现代码:

  • Java线程:不用条件变量

李四存款3600当前余额为13600

結合先前同步代码知识,举一反三将此例改为同步代码块来实现,代码如下:

  • Java线程:改为同步代码块

李四存款3600当前余额为13600

对比以上三種方式,从控制角度上讲第一种最灵活,第二种代码最简单第三种容易犯错。

Java线程:新特征-原子量

为何要使用原子变量呢原因是多個线程对单个变量操作也会引起一些问题。在Java5之前可以通过volatile、synchronized关键字来解决并发访问的安全问题,但这样太麻烦

Java5之后,专门提供了用來进行单变量多线程并发安全访问的工具包java.util.concurrent.atomic其中的类也很简单。

下面给出一个反面例子(切勿模仿):

  • Java线程:新特征-原子量

李四执行了3600当前余额:13600

张三执行了2000,当前余额:12000

张三执行了2000当前余额:12000

从运行结果可以看出,虽然使用了原子量但是程序并发访问还是有问题,那究竟问题出在哪里了

这里要注意的一点是,原子量虽然可以保证单个变量在某一个操作过程的安全但无法保证你整个代码块,或鍺整个程序的安全性因此,通常还应该使用锁等同步机制来控制整个程序的安全性

下面是对这个错误修正:

  • Java线程:新特征-原子量

张三執行了2000,当前余额:12000

这里使用了一个对象锁来控制对并发代码的访问。不管运行多少次执行次序如何,最终余额均为21000这个结果是正確的。

有关原子量的用法很简单关键是对原子量的认识,原子仅仅是保证变量操作的原子性但整个程序还需要考虑线程安全的。

Java线程:新特征-障碍器

障碍器是多线程并发控制的一种手段用法很简单。下面给个例子:

  • Java线程:新特征-障碍器
  • //通知障碍器已经完成

[子任务E]开始執行了!

从执行结果可以看出所有子任务完成的时候,主任务执行了达到了控制的目标。

我要回帖

更多关于 信号量 wait和signal 的文章

 

随机推荐