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

一、实验目的和要求

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

  • 18
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
多线程编程中,多个线程可以同时运行,但是它们可能需要访问同一资源,这时就需要进行同步互斥,以保证程序的正确性。常用的方法包括使用信号量、互斥锁和条件变量。 1. 信号量 信号量是一种在多线程编程中实现同步方法。信号量记录了某个共享资源的状态,用于控制线程的执行顺序和访问资源的方式。信号量有两种类型:二进制信号量和计数信号量。 二进制信号量只有两种状态:0和1。当信号量为0时,线程会被阻塞。当信号量为1时,线程可以继续执行。二进制信号量通常用于实现互斥。 计数信号量可以有多种状态。当信号量为0时,线程会被阻塞。当信号量为正数时,线程可以继续执行。当信号量为负数时,线程会被阻塞,并且可以唤醒其他线程。计数信号量通常用于实现同步。 2. 互斥互斥锁是一种在多线程编程中实现互斥方法互斥锁是一个二进制标志,用于控制某个共享资源只有一个线程可以访问。当一个线程获得了互斥锁,其他线程需要等待,直到该线程释放互斥锁。 在使用互斥锁时,需要注意避免死锁的问题。死锁发生在多个线程互相等待对方释放资源的情况下。为了避免死锁,可以遵循以下原则: - 一个线程只能持有一个锁,否则容易出现死锁。 - 尽量避免在锁内部执行耗时操作。 - 获得锁的顺序应该是固定的,例如按照某个资源的编号来获得锁。 3. 条件变量 条件变量是一种在多线程编程中实现同步方法。条件变量用于实现线程的等待和唤醒,以实现同步互斥。条件变量通常与互斥锁一起使用,以实现线程的同步。 在使用条件变量时,需要注意避免竞态条件的问题。竞态条件发生在多个线程同时访问共享资源,并且对资源的访问顺序不确定的情况下。为了避免竞态条件,可以使用互斥锁来保护共享资源,并使用条件变量来实现线程的等待和唤醒。 总之,使用信号量、互斥锁和条件变量等方法可以有效地解决多线程编程中的同步互斥问题。但是,在使用这些方法时,需要注意避免死锁和竞态条件等问题

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值