操作系统实验报告-多线程编程解决进程间同步和互斥问题
一、实验目的和要求
掌握并发进程中同步与互斥的机制,基于线程的编程技术开发类似生产者—消费者问题的演示程序。
熟练掌握一种以上的开发工具,如C++、JAVA、Delphi、VB等,掌握基本的编程技巧,自由选择一种编程语言设计并实现实验内容。
二、实验方法与步骤(需求分析、算法设计思路、流程图等)
需求分析
:生产者/消费者问题是一个典型的进程同步问题,现通过买家通过买包子的案例模拟进程的同步。消费者与生产者进程之间的执行都依赖于另一个进程的消息,表现同步,这需要使用Java中的wait() / notify()方法实现同步机制。由于包子余量需要所有进程共享,因此任意时刻只能有一个进程访问缓冲器,这需要使用Java中的synchronized同步代码块实现,如果方法或代码块用 synchronized 进行声明,那么对象的锁将保护整个方法或代码块,要调用这个方法或者执行这个代码块,必须获得这个对象的锁。而且,任何时候都只能有一个线程对象执行被保护的代码。
算法设计思路
:现设计2个厨师(生产者),2个买家(消费者),店家包子存储上限为3(缓冲器大小)。包子初始值为0,此时所有买家进程会进入等待状态,所有的厨师进程会在包子余量不超过缓冲器大小前不停做包子,并唤醒买家进程已经有包子可吃了,直至缓冲器满了进入等待状态,而买家进程每吃掉一个包子后都会唤醒厨师进程可以继续做包子了。同时由于包子余量需要所有进程共享,保证任意时刻只能有一个进程访问缓冲器,所有进程方法都会用synchronized声明。
流程图
:
三、实验原始纪录(源程序、数据结构等)
1.BaoZi类(定义缓冲器余量,生产者和消费者执行方法):
public class BaoZi {
Integer count = 0; //包子剩余量,最大为3
public synchronized void BaoZiPu(String name) {
while (count >= 3) {
System.out.println(name+":包子已生产上限啦!");
try {
wait(); //包子剩余超过3,厨师进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count++;
System.out.println(name + "做了一个包子,剩余:" + count);
notifyAll(); //有包子了,唤醒消费者可以吃了
}
public synchronized void customer(String name) {
while (count == 0) {
System.out.println(name+":包子没有啦!");
try {
wait(); //包子余量为0,消费者进入等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println(name+"吃了一个包子,剩余:" + count);
notifyAll(); //包子未达上限,唤醒厨师可以继续做包子
}
}
2.BaoZiPu类(生产者,定义厨师名称,调用生产者方法):
public class BaoZiPu extends Thread {
private String name;
private BaoZi bz;
public BaoZiPu(){}
public BaoZiPu(BaoZi bz, String name){
this.bz = bz;
this.name = name;
}
@Override
public void run(){
while(true){
try{
Thread.sleep(2000);
bz.BaoZiPu(name); //执行生产者方法
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
3.Customer类(消费者,定义买家名字,调用消费者方法):
public class Customer extends Thread {
private BaoZi bz;
private String name;
public Customer(){}
public Customer(BaoZi bz, String name){
this.bz = bz;
this.name = name;
}
@Override
public void run(){
while(true){
try{
Thread.sleep(2000);
bz.customer(name);//执行消费者方法
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
4.测试代码:
public class DemoBaoZi {
public static void main(String[] args) {
BaoZi bz = new BaoZi();//所用进程共用同一个bz对象作为同步代码块的锁,他们共享了包子余量即缓冲器资源,同时也保证了任意时间内只有一个进程再访问缓冲器
Thread p1 = new Thread(new BaoZiPu(bz, "大包子厨师"));
Thread p2 = new Thread(new BaoZiPu(bz, "小笼包厨师"));
Thread c1 = new Thread(new Customer(bz,"小红"));
Thread c2 = new Thread(new Customer(bz,"小明"));
p1.start();
c1.start();
p2.start();
c2.start();
}
}
四、实验结果及分析(计算过程与结果、数据曲线、图表等)
实验结果分析
:根据结果分析,他们共享缓存器资源包子余量count,首先是p1被执行,包子加1,余量为1。接着c1被执行,包子减1,余量为0 。其余步骤类似,直至第11步,此时c1执行,发现缓存器中包子余量为0,进入等待状态并唤醒生产者。同理可知,如果某个时刻执行生产者进程时,发现缓存器中包子余量为3,则该生产者进程也会进入等待状态并唤醒消费者。
五、实验改进与思考
通过实验学习和代码实践,让我对进程间同步和互斥有了更深一步的理解和认识。通过包子铺卖包子和买家买包子的案例模拟生产者/消费者问题实现进程间的同步和互斥。对于包子铺能不能生产包子放到缓冲器中,需要两个条件。首先时缓冲器需要空闲,即包子余量有没有达到上限,还需要判断有没有其他生产者或消费者在缓冲器中,这需要synchronized同步代码块实现。
对于本次实验的代码实现中,我多次因为概念不清,要求不明走了许多弯路,例如错误设置两个缓冲器导致共享变量结果与预期不符,还有由于是多个生产者和消费者所以在执行唤醒操作时应当使用notifyAll,而我错误使用notify导致个别进程一直处于等待。实质上,很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式,而这是恰恰是在操作系统课堂上老师反复讲解,而我们却视而不见不以为然的。因此我应当反复再对这部分内容进行巩固学习,多多实践来加深这部分知识的理解和学习。