1.临界资源:一次只能允许一个进程访问的资源;
临界区:访问临界资源的代码段,不允许多个并发进程交叉执行的代码段
2.进程之间的制约关系
(1)由于进程之间共享资源而引起的间接制约关系(互斥)
(2)由于进程之间相互协作而引起的直接制约关系(同步)//当且仅当一个进程执行完毕,另一个进程才能被执行
3.如何实现临界区的互斥访问?
在临界区之前加进入区,在临界区之后加退出区,在进入区中先检查临界资源是否被访问,如果没有被访问则直接进入临界区,并将临界区的状态置为忙,如果临界区资源被访问,则在时钟管理的控制下每隔一定时间去检测临界区资源是否被访问;在退出区中恢复临界区的状态为空闲。
4.同步机制应遵循的准则
(1)空闲等待
(2)忙则等待:连续测量某个变量,直到某个值出现为止
(3)有限等待
(4)让权等待:临界区运行的进程不得阻塞其它进程,需要让出cpu
5.什么是信号量,简述其含义?
信号量是对系统资源及其使用情况的抽象,由一个记录型的数据表示。
a.为什么要引入信号量?
(1)硬件方法存在的缺点之一就是循环测试锁的状态,损耗cpu的时间;
(2)第二个缺点是从等待进程中随机选取一个进程进入临界区,有的进程可能永远都选不上。会存在不公平的现象。
举个例子
如:几个班排队在教室举行活动,一班先过来,发现教室在空闲着,就进去了开始举行活动,二班再过来发现教室被占用着于是在时钟管理的控制下每隔一定时间过来检查教室是否在空闲,三班也过来了发现教室还被占着于是也在时钟管理的控制下每隔一定时间过来检查教室是否在空闲,二、三班每次在来检查的时候教室都在被占着,而此时四班来了,恰巧此时一班刚好用完教室,那么四班只来检测了一次就用上了教室,显然是不公平的,而在此期间,二班、三班一直来检查教室使用的状态,显然是很浪费时间的,于是我们可以引入教室监管员(信号量),教室监管员给每次来排队用教室的班级发一把钥匙,将它们合理的组织起来,在合适的时间将它们唤醒。
b.信号量的数据结构
信号量的数据结构是一个值和一个指针,指针指向等待信号量的下一个进程,值与资源的使用情况有关。
typedef struct {
int value;//value表示可用资源的数目
*pcb L;//L表示为等待此类资源的进程PCB表链
}semaphore;
semaphore s;
value > 0:表示有value的可用资源;
value = 0:表示资源正好用完;
value < 0:表示有|value|个进程正在等待当前进程。
6.PV操作
(1)什么是PV操作?
P操作:wait(s);
功能:请求系统分配一个单位的资源,进程进入;
参数:信号量s
流程:
-- s.value; //表示申请一个资源;
if (s.value <0) //表示没有空闲资源;
block(S.L); // 调用进程进入等待队列,阻塞调用进程;
如果s.value < 0,则说明有可用的资源,则进入临界区,s.value = s.value - 1;
如果s.value >= 0,则说明没有可用资源,则会将进程挂入等待队列,循环等待,s.value = s.value - 1。
V操作:signal(s)
功能:释放一个单位的资源
参数:信号量s
流程:
++S.value; //表示释放一个资源;
if (S.value <= 0) //表示仍有进程处于阻塞状态;
wakeup(S.L);// 从等待队列S.L中取出一个进程P;
// 进程P进入就绪队列;
s.value <= 0:唤醒s.L中的一个进程;
s.value > 0:说明无阻塞进程,有剩余段可继续执行,没有就就进程。
实例
(1)写一个教室的类
package edu.xalead.线程_进程;
public class 教室 {
public int classroomNum = 10;//包子数
}
(2)写一个用教室的班级的类
package edu.xalead.线程_进程;
public class 用教室的班级 extends Thread {
private 教室 classroom = null;
private String classes = null;
public 用教室的班级(教室 classroom ,String classes ){
this.classroom = classroom;
this.classes = classes ;
}
public void run(){
while (true){
if(this.classroom.classroomNum <= 0) break; //也就是共享资源用完
System.out.println(this.classes + "用第" + this.classroom.classroomNum + "个班级");
this.classroom.classroomNum--;
}
}
}
(3)写一个测试类
package edu.xalead.线程_进程;
public class 测试用教室 {
public static void main(String[] args) {
教室 classroom = new 教室();
用教室的班级 c1 = new 用教室的班级(classroom ,"一班");
用教室的班级 c2 = new 用教室的班级(classroom ,"二班");
用教室的班级 c3 = new 用教室的班级(classroom ,"三班");
用教室的班级 c4 = new 用教室的班级(classroom ,"四班");
c1.start();
c2.start();
c3.start();
c4.start();
}
}
(4)输出结果
由输出结果可以看出问题很显然已经暴露了出来
a.班级被重复占用;
b.三班、四班始终用不上班级。
原因:多个线程访问同一个资源的时候,会出现线程争用的问题。
解决方案:(引入教室监管员,将教室看做监听对象)
(1)加锁
package edu.xalead.线程_进程;
public class 用教室的班级 extends Thread {
private 教室 classroom = null;
private String classes = null;
public 用教室的班级(教室 classroom ,String classes ){
this.classroom = classroom;
this.classes = classes ;
}
public void run(){
try {
while (true){
synchronized (classroom) { //将教室看做监听对象
if (this.classroom.classroomNum <= 0) break; //也就是共享资源用完
System.out.println(this.classes + "用第" + this.classroom.classroomNum + "个班级");
this.classroom.classroomNum--;
}
Thread.sleep(10);//让当前线程休息一会,否则如果当前线程的时间没有用完就会一直执行当前线程
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果
(2)同步方法
package edu.xalead.线程_进程;
public class 教室 {
public int classroomNum = 10;//包子数
public synchronized void yongjiaoshi(String classes){
System.out.println(classes + "用第" + this.classroomNum + "个班级");
this.classroomNum--;
}
}
package edu.xalead.线程_进程;
public class 用教室的班级 extends Thread {
private 教室 classroom = null;
private String classes = null;
public 用教室的班级(教室 classroom ,String classes ){
this.classroom = classroom;
this.classes = classes ;
}
public void run(){
try {
while (true){
if (this.classroom.classroomNum <= 0) break; //也就是共享资源用完
classroom.yongjiaoshi(this.classes);
Thread.sleep(10);//让当前线程休息一会,否则如果当前线程的时间没有用完就会一直执行当前线程
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果