0


操作系统生产者-消费者问题(PV操作)(Java实现)

一、问题描述
一组生产者进程和一组消费者进程共享一个初始为空、大小n的缓冲区,只有缓冲区没满时,生产者才能把资源放入缓冲区,否则必须等待;只有缓冲区不为空时,消费者才能从中取出资源,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入资源,或一个消费者从中取出资源。

二、问题分析
(1)、关系分析。生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者又是一个相互协作的关系,只有生产者生产之后,消费者只能才能消费,它们还是同步关系。
(2)、整理思路。只有生产生产者和消费者进程,正好是这两个进程存在着互斥关系和同步关系,即需要解决的是互斥和同步 PV 操作的位置。
(3)、信号量设置。信号量 mutex 作为互斥信号量,用于控制互斥访问缓冲池,互斥信号量初值为1;信号量 full 用于记录当前缓冲池中的“满”缓冲池,初值为0;信号量 empty 用于记录当前缓冲池中“空“缓冲区数,初值为n。

三、代码实现

import java.util.Scanner;publicclassProCon{publicstaticvoidmain(String[] args){int producer,consumer;
        Scanner sc=newScanner(System.in);
        System.out.print("请输入生产者数目:");
        producer=sc.nextInt();//输入生产者数量
        System.out.print("请输入消费者数目:");
        consumer=sc.nextInt();//输入消费者数量for(int i=0;i<producer;i++){newThread(newProducer(),"生产者"+ Integer.toString(i)+"号").start();//创建生产者线程并开启}for(int j=0;j<consumer;j++){newThread(newConsumer(),"消费者"+ Integer.toString(j)+"号").start();//创建消费者线程并开启}}}classGlobal{publicstatic Semaphore empty=newSemaphore(3);//空闲缓冲区初始化为三publicstatic Semaphore full=newSemaphore(0);//满缓冲区初始化为空publicstatic Semaphore mutex=newSemaphore(1);//临界区互斥信号量publicstaticint count=0;//count用于缓冲区中的进程进行计数//定时等待publicstaticvoidtimingwait(){try{
            Thread.sleep(2000);//Thread.Sleep()方法用于将当前线程休眠一定时间 时间单位是ms,1s=1000ms}catch(InterruptedException e)//当使用java.lang.Thread类的sleep方法时,可能会导致线程阻塞,需要抛出InterruptedException(中断异常)异常{
            e.printStackTrace();}}}//生产者classProducerimplementsRunnable//Runnable接口创建新线程{@Overridepublicvoidrun()//Runnable 接口可以被任何想要被一个线程运行的接口继承实现;继承 Runnable 接口的类必须有一个 run() 方法{
        Global.timingwait();
        System.out.println(Thread.currentThread().getName()+"  生产出一个商品...");//Thread.currentThread().getName()获得当前执行的线程
        Global.empty.P();//获取空缓冲区单元
        Global.mutex.P();//进入临界区
        Global.timingwait();
        System.out.println(Thread.currentThread().getName()+"  将产品放入缓冲区--缓冲区剩余 "+(++Global.count)+" 个产品");
        Global.mutex.V();//离开临界区,释放信号量
        Global.full.V();//满缓冲区数加一}}//消费者classConsumerimplementsRunnable{@Overridepublicvoidrun(){
        Global.timingwait();
        Global.full.P();//获取满缓冲区单元
        Global.mutex.P();//进入临界区
        Global.timingwait();
        System.out.println(Thread.currentThread().getName()+"  从缓冲区取出一个产品--缓冲区剩余 "+(--Global.count)+" 个产品");
        Global.mutex.V();//离开临界区,释放互斥信号量
        Global.empty.V();//空缓冲区加一
        System.out.println(Thread.currentThread().getName()+"  消费一个商品...");}}//信号量classSemaphore{publicint value;publicSemaphore(int value){super();this.value=value;}//P操作publicsynchronizedfinalvoidP()//使用synchronized修饰的方法,叫做同步方法,保证A线程执行该方法的时,其他线程只能在方法外等着.{//被final修饰的方法是一个最终方法,不能被重写,重写会报错
        value--;if(value<0){try{this.wait();//当缓冲区已满/空时,生产者或消费者线程停止自己的执行,释放锁,使自己处于等待状态,让其它线程执行}catch(InterruptedException e)//当使用java.lang.Thread类的 wait方法时,可能会导致线程阻塞,需要抛出InterruptedException(中断异常)异常{
                e.printStackTrace();}}}//V操作publicsynchronizedfinalvoidV(){
        value++;if(value<=0){this.notify();//当生产者或消费者向缓冲区放入或取出一个产品时,向其他等待的线程发出通知,同时释放锁,使自己处于等待状态,让其它线程执行。}}}

四、运行效果
在这里插入图片描述
(运行结果是以缓冲区大小为3,消费者数量为2和生产者数量为3为初始值)

五、知识总结

1、该类问题需要注意对缓冲区大小为n的处理,当缓冲区中有空时,便可对 empty 变量执行 P 操作,一旦取走一个资源便可执行 V 操作以释放空闲区。对 empty 和 full 变量的 P 操作 必须放在 mutex 的P操作之前。
2、P操作即wait操作表示进程请求一个资源,V操作即signal表示进程释放一个资源,且信号量机制遵循了同步机制的“让权等待”原则。
3、wait():当缓冲区已满/空时,生产者或消费者线程停止自己的执行,释放锁,使自己处于等待状态,让其它线程执行。
notify():当生产者或消费者向缓冲区放入或取出一个产品时,向其他等待的线程发出通知,同时释放锁,使自己处于等待状态,让其它线程执行。
wait()、nofity()这两个方法必须有锁对象调用,而任意对象都可以作为 synchronized 的同步锁。
4、使用synchronized修饰的方法,叫做同步方法,保证A线程执行该方法的时,其他线程只能在方法外等待。
被final修饰的方法是一个最终方法,不能被重写,重写会报错。


本文转载自: https://blog.csdn.net/qq_44111805/article/details/112002062
版权归原作者 Hard Coder 所有, 如有侵权,请联系我们删除。

“操作系统生产者-消费者问题(PV操作)(Java实现)”的评论:

还没有评论