操作系统实验报告-多线程编程解决进程间同步和互斥问题

  • Post author:
  • Post category:其他




一、实验目的和要求

掌握并发进程中同步与互斥的机制,基于线程的编程技术开发类似生产者—消费者问题的演示程序。

熟练掌握一种以上的开发工具,如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导致个别进程一直处于等待。实质上,很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式,而这是恰恰是在操作系统课堂上老师反复讲解,而我们却视而不见不以为然的。因此我应当反复再对这部分内容进行巩固学习,多多实践来加深这部分知识的理解和学习。



版权声明:本文为weixin_47532313原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。