zoukankan      html  css  js  c++  java
  • Java实现PV操作 | 生产者与消费者

    • 导语

    在学习操作系统的过程中,PV操作是很重要的一个环节。然而面对书本上枯燥的代码,每一个爱好技术的人总是想能亲自去实现。现在我要推出一个专题,专门讲述如何用Java实现PV操作,让操作系统背后的逻辑跃然屏上。

    如有错误,请广大网友斧正,感激不尽!


    经典问题1、生产者与消费者

     

    • PV操作数据结构的构建

    在书本上,我们给出了一种数据结构,叫做信号量。这种信号量有两个元素:

      一个是count,如果是正值则表示当前资源的个数,如果是0,表示有一个进程在执行临界区的代码(也就是说这个进程位于临界区);并且没有进程处于阻塞队列中。如果是负值,这个值的绝对值(abs(count))表示阻塞队列中进程的个数。

      一个是queue,即为阻塞进程队列。当进程不能申请相应的资源是,则使用P操作,将自己插入阻塞队列中。当运行的进程执行完临界区代码时,就执行V操作,唤醒一个阻塞队列中的进程。

     

    现在我们定义一个PV操作类:syn。在这个类中我们可以通过构造函数设置count的值。可以看到这个类中并没有阻塞进程所在的queue,我是通过java的this.wait()this.notifyAll()来实现的。

    并且,通过关键字【synchronized】,保证了PV操作是一条【原语】,即在运行过程中,占有完整的一个时间片,不可分割。

     1 class syn{//PV操作类
     2     int count=0;//信号量
     3     syn(){}
     4     syn(int a){count=a;}
     5     public synchronized void Wait(){ //关键字 synchronized 保证了此操作是一条【原语】
     6         count--;
     7         if(count<0){//等于0 :有一个进程进入了临界区
     8             try {         //小于0:abs(count)=阻塞的进程数目
     9                 this.wait();
    10             } catch (InterruptedException e) {
    11                 e.printStackTrace();  
    12             }  
    13         }  
    14     }  
    15     public synchronized void Signal(){   //关键字 synchronized 保证了此操作是一条【原语】
    16         count++;
    17         if(count<=0){//如果有进程阻塞
    18             this.notify();
    19         }
    20     }  
    21 }

     注1:P操作wait中,应为if(count<0)而不是while(count<0) 。在后续的编写中,发现如果引入了多个生产者与消费者,用语句while(count<0) 就会出错。

     注2:V操作signal中,应为this.notify()而不是this.notifyAll()。也就是说只需要从阻塞队列中唤醒一个进程。


    • 单个生产者与消费者的模型构建

    我们构建实现Runnable接口的生产者类和消费者类,使用empty(表示空缓冲区的数目)和full(表示满缓冲区的数目)两个信号量来实现进程的同步。(不同类型的进程共享某一资源为同步关系

    代码如下:

     

     1 public class Main {
     2 
     3     public static void main(String[] args) {
     4         Producer p=new Producer();
     5         Consumer c=new Consumer();
     6         Thread pp=new Thread(p);
     7         Thread cp=new Thread(c);
     8         pp.start();
     9         cp.start();
    10     }
    11 }
    12 
    13 class Global{
    14     static syn empty=new syn(8);
    15     static syn full=new syn(0);
    16     static int buffer []=new int[8];//缓冲区
    17 }
    18 
    19 //生产者类
    20 class Producer implements Runnable{
    21     int count=0;
    22     public void run(){
    23         while(count<20){
    24             Global.empty.Wait();
    25             //临界区
    26             int index=count%8;
    27             Global.buffer[index]=count;
    28             System.out.println("生产者在缓冲区"+index+"中生产了物品"+count);
    29             count++;
    30             try {
    31                 Thread.sleep(10);
    32             } catch (InterruptedException e) {
    33                 // TODO Auto-generated catch block
    34                 e.printStackTrace();
    35             }
    36             // end of 临界区
    37             Global.full.Signal();
    38         }
    39     }
    40 }
    41 
    42 //消费者类
    43 class Consumer implements Runnable{
    44     int count=0;
    45     public void run(){
    46         while(count<20){
    47             Global.full.Wait();
    48             //临界区
    49             int index=count%8;
    50             int value=Global.buffer[index];
    51             System.out.println("消费者在缓冲区"+index+"中消费了物品"+value);
    52             count++;
    53             try {
    54                 Thread.sleep(10);
    55             } catch (InterruptedException e) {
    56                 // TODO Auto-generated catch block
    57                 e.printStackTrace();
    58             }
    59             // end of 临界区
    60             Global.empty.Signal();
    61         }
    62     }
    63 }

     

    运行结果:

     可以看出进程严格按照先生产再消费的顺序,完美运行。


     

    • 引入多个生产者与消费者

    为保证同一类进程在进行访问时能保证互斥互斥是同类进程共享某一资源时的方式)我们引入mutex信号量。

        static syn pMutex=new syn(1);//保证生产者之间互斥
        static syn cMutex=new syn(1);//保证消费者之间互斥

     运行结果:

     

    可见程序完美运行。


    完整Java代码:

      1 public class Main {
      2 
      3     public static void main(String[] args) {
      4         Producer p[]=new Producer[3];//3个生产者
      5         Consumer c[]=new Consumer[3];
      6         int i;
      7         
      8         for(i=0;i<3;i++){
      9             p[i]=new Producer(i+1);
     10         }
     11         for(i=0;i<3;i++){
     12             c[i]=new Consumer(i+1);
     13         }
     14         
     15         Thread pp[]=new Thread[3];
     16         Thread cp[]=new Thread[3];
     17         
     18         for(i=0;i<3;i++){
     19             pp[i]=new Thread(p[i]);
     20         }
     21         for(i=0;i<3;i++){
     22             cp[i]=new Thread(c[i]);
     23         }
     24         
     25         for(i=0;i<3;i++){
     26             pp[i].start();
     27         }
     28         for(i=0;i<3;i++){
     29             cp[i].start();
     30         }
     31 
     32     }
     33 }
     34 
     35 class Global{
     36     static syn empty=new syn(8);
     37     static syn full=new syn(0);
     38     static syn pMutex=new syn(1);//保证生产者之间互斥
     39     static syn cMutex=new syn(1);//保证消费者之间互斥
     40     static int buffer []=new int[8];//缓冲区
     41     static int pCount=0;
     42     static int cCount=0;
     43 }
     44 
     45 //生产者类
     46 class Producer implements Runnable{
     47     int ID=0;
     48     Producer(){}
     49     Producer(int id){ID=id;}
     50     public void run(){
     51         while(Global.pCount<20){
     52             Global.empty.Wait();
     53             Global.pMutex.Wait();
     54             //临界区
     55             int index=Global.pCount%8;
     56             Global.buffer[index]=Global.pCount;
     57             System.out.println("生产者"+ID+"在缓冲区"+index+"中生产了物品"+Global.pCount);
     58             Global.pCount++;
     59             try {
     60                 Thread.sleep(10);
     61             } catch (InterruptedException e) {
     62                 // TODO Auto-generated catch block
     63                 e.printStackTrace();
     64             }
     65             // end of 临界区
     66             Global.pMutex.Signal();
     67             Global.full.Signal();
     68         }
     69     }
     70 }
     71 
     72 //消费者类
     73 class Consumer implements Runnable{
     74     int ID=0;
     75     Consumer(){}
     76     Consumer(int id){ID=id;}
     77     public void run(){
     78         while(Global.cCount<20){
     79             Global.full.Wait();
     80             Global.cMutex.Wait();
     81             //临界区
     82             int index=Global.cCount%8;
     83             int value=Global.buffer[index];
     84             System.out.println("消费者"+ID+"在缓冲区"+index+"中消费了物品"+value);
     85             Global.cCount++;
     86             try {
     87                 Thread.sleep(10);
     88             } catch (InterruptedException e) {
     89                 // TODO Auto-generated catch block
     90                 e.printStackTrace();
     91             }
     92             // end of 临界区
     93             Global.cMutex.Signal();
     94             Global.empty.Signal();
     95         }
     96     }
     97 }
     98 
     99 class syn{//PV操作类
    100     int count=0;//信号量
    101     syn(){}
    102     syn(int a){count=a;}
    103     public synchronized void Wait(){ //关键字 synchronized 保证了此操作是一条【原语】
    104         count--;
    105         if(count<0){//等于0 :有一个进程进入了临界区
    106             try {         //小于0:abs(count)=阻塞的进程数目
    107                 this.wait();
    108             } catch (InterruptedException e) {
    109                 e.printStackTrace();  
    110             }  
    111         }  
    112     }  
    113     public synchronized void Signal(){   //关键字 synchronized 保证了此操作是一条【原语】
    114         count++;
    115         if(count<=0){//如果有进程阻塞
    116             this.notify();//All
    117         }
    118     }  
    119 }

  • 相关阅读:
    win10使用4G 模块RNDIS模式上网
    转]GSM模块信号强度CSQ与RSSI的对应关系
    /etc/inittab文件详解
    网口扫盲一:网卡初步认识
    网口扫盲二:Mac与Phy组成原理的简单分析
    网口扫盲三:以太网芯片MAC和PHY的关系
    【 MAKEFILE 编程基础之四】详解MAKEFILE 函数的语法与使用!
    【 MAKEFILE 编程基础之三】详解 MAKEFILE 变量的定义规则使用!
    转载:基于jquery的bootstrap在线文本编辑器插件Summernote
    HTML特殊符号对照表、常用的字符实体
  • 原文地址:https://www.cnblogs.com/TQCAI/p/7700354.html
Copyright © 2011-2022 走看看