• 导语

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

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


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

 

  • PV操作数据结构的构建

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

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

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

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

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

 class syn{//PV操作类
int count=0;//信号量
syn(){}
syn(int a){count=a;}
public synchronized void Wait(){ //关键字 synchronized 保证了此操作是一条【原语】
count--;
if(count<0){//等于0 :有一个进程进入了临界区
try { //小于0:abs(count)=阻塞的进程数目
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void Signal(){ //关键字 synchronized 保证了此操作是一条【原语】
count++;
if(count<=0){//如果有进程阻塞
this.notify();
}
}
}

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

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


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

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

代码如下:

 public class Main {

     public static void main(String[] args) {
Producer p=new Producer();
Consumer c=new Consumer();
Thread pp=new Thread(p);
Thread cp=new Thread(c);
pp.start();
cp.start();
}
} class Global{
static syn empty=new syn(8);
static syn full=new syn(0);
static int buffer []=new int[8];//缓冲区
} //生产者类
class Producer implements Runnable{
int count=0;
public void run(){
while(count<20){
Global.empty.Wait();
//临界区
int index=count%8;
Global.buffer[index]=count;
System.out.println("生产者在缓冲区"+index+"中生产了物品"+count);
count++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end of 临界区
Global.full.Signal();
}
}
} //消费者类
class Consumer implements Runnable{
int count=0;
public void run(){
while(count<20){
Global.full.Wait();
//临界区
int index=count%8;
int value=Global.buffer[index];
System.out.println("消费者在缓冲区"+index+"中消费了物品"+value);
count++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end of 临界区
Global.empty.Signal();
}
}
}

运行结果:

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


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

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

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

运行结果:

可见程序完美运行。


完整Java代码:

 public class Main {

     public static void main(String[] args) {
Producer p[]=new Producer[3];//3个生产者
Consumer c[]=new Consumer[3];
int i; for(i=0;i<3;i++){
p[i]=new Producer(i+1);
}
for(i=0;i<3;i++){
c[i]=new Consumer(i+1);
} Thread pp[]=new Thread[3];
Thread cp[]=new Thread[3]; for(i=0;i<3;i++){
pp[i]=new Thread(p[i]);
}
for(i=0;i<3;i++){
cp[i]=new Thread(c[i]);
} for(i=0;i<3;i++){
pp[i].start();
}
for(i=0;i<3;i++){
cp[i].start();
} }
} class Global{
static syn empty=new syn(8);
static syn full=new syn(0);
static syn pMutex=new syn(1);//保证生产者之间互斥
static syn cMutex=new syn(1);//保证消费者之间互斥
static int buffer []=new int[8];//缓冲区
static int pCount=0;
static int cCount=0;
} //生产者类
class Producer implements Runnable{
int ID=0;
Producer(){}
Producer(int id){ID=id;}
public void run(){
while(Global.pCount<20){
Global.empty.Wait();
Global.pMutex.Wait();
//临界区
int index=Global.pCount%8;
Global.buffer[index]=Global.pCount;
System.out.println("生产者"+ID+"在缓冲区"+index+"中生产了物品"+Global.pCount);
Global.pCount++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end of 临界区
Global.pMutex.Signal();
Global.full.Signal();
}
}
} //消费者类
class Consumer implements Runnable{
int ID=0;
Consumer(){}
Consumer(int id){ID=id;}
public void run(){
while(Global.cCount<20){
Global.full.Wait();
Global.cMutex.Wait();
//临界区
int index=Global.cCount%8;
int value=Global.buffer[index];
System.out.println("消费者"+ID+"在缓冲区"+index+"中消费了物品"+value);
Global.cCount++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end of 临界区
Global.cMutex.Signal();
Global.empty.Signal();
}
}
} class syn{//PV操作类
int count=0;//信号量
syn(){}
syn(int a){count=a;}
public synchronized void Wait(){ //关键字 synchronized 保证了此操作是一条【原语】
count--;
if(count<0){//等于0 :有一个进程进入了临界区
try { //小于0:abs(count)=阻塞的进程数目
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void Signal(){ //关键字 synchronized 保证了此操作是一条【原语】
count++;
if(count<=0){//如果有进程阻塞
this.notify();//All
}
}
}

Java实现PV操作 | 生产者与消费者的更多相关文章

  1. PV操作——生产者和消费者

    首先,先来看几个概念: 同步:协作的过程,比如,多人开发合作. 相互排斥:争抢资源的过程.比如苦逼的大学选课: 临界区:进程中对临界资源实施操作的那段程序: 临界资源:一次仅仅能一个进程使用的资源,比 ...

  2. java信号量PV操作 解决生产者-消费者问题

    package test1; /** * 该例子演示生产者和消费者的问题(设只有一个缓存空间.一个消费者和一个生产者) * MySystem类定义了缓冲区个数以及信号量 * @author HYY * ...

  3. java多线程中的生产者与消费者之等待唤醒机制@Version1.0

    一.生产者消费者模式的学生类成员变量生产与消费demo,第一版1.等待唤醒:    Object类中提供了三个方法:    wait():等待    notify():唤醒单个线程    notify ...

  4. Java并发编程(4)--生产者与消费者模式介绍

    一.前言 这种模式在生活是最常见的,那么它的场景是什么样的呢? 下面是我假象的,假设有一个仓库,仓库有一个生产者和一个消费者,消费者过来消费的时候会检测仓库中是否有库存,如果没有了则等待生产,如果有就 ...

  5. Java 线程的通讯--生产者和消费者

    package 生产者和消费者; //消费者 public class Customer implements Runnable { private Share_resources rescource ...

  6. Java多线程设计模式(2)生产者与消费者模式

    1 Producer-Consumer Pattern Producer-Consumer Pattern主要就是在生产者与消费者之间建立一个“桥梁参与者”,用来解决生产者线程与消费者线程之间速度的不 ...

  7. Java多线程与并发——生产者与消费者应用案例

    多线程的开发中有一个最经典的操作案例,就是生产者-消费者,生产者不断生产产品,消费者不断取走产品. package com.vince; /** * 生产者与消费者案例 * @author Admin ...

  8. Java实现PV操作 | 哲学家进餐问题

    运行结果: Java代码: public class Main { public static void main(String[] args) { Global global=new Global( ...

  9. Java实现PV操作 | 读者与写者(在三种情况下进行讨论)

    注 :本文应结合[天勤笔记]进行学习. 1.读者优先 设置rmutex信号量来对readcount变量进行互斥访问.mutex信号量对写者与读者进行同步. static syn rmutex=new ...

随机推荐

  1. MQTT --- Retained Message

    保留消息定义 如果PUBLISH消息的RETAIN标记位被设置为1,则称该消息为“保留消息”: Broker会存储每个Topic的最后一条保留消息及其Qos,当订阅该Topic的客户端上线后,Brok ...

  2. UDP比TCP好用的优势

    网络带宽环境变好 在2007年至2015年间,网络的带宽飞速发展,从1.5Mbps的带宽增加到5.1Mbps的带宽,足足增加了4倍,网络环境快速.稳定,所以UDP的丢包率 下降至5%以下,越来越好的网 ...

  3. 集成开发环境(IDE)

    学习目标: 1.了解Java的IDE开发工具 2.会使用Eclipse.IDEA开发工具新建项目,编写代码,并运行程序. 学习过程: 使用文本开发效率无疑是很低的,每次编写完代码后,还需要手动的编译执 ...

  4. latex制作表格-跨行跨列

    1.列的合并,使用 \multicolumn{跨几列}{格式}{填充内容} \documentclass[UTF8]{ctexart} \begin{document} 三囚犯问题进行300次实验后\ ...

  5. 关于.net core 中的signalR组件的使用

    SignalR是为了提供更方便的web交互响应式到推送式的解决方案.有了它之后可以实现客户端直接调用服务端的方法并且获得返回值 (客户端可以是各种平台,目前SignalR支持的语言版本有C#.java ...

  6. Java自学-数字与字符串 字符串

    Java中的字符串String 示例 1 : 创建字符串 字符串即字符的组合,在Java中,字符串是一个类,所以我们见到的字符串都是对象 常见创建字符串手段: 每当有一个字面值出现的时候,虚拟机就会创 ...

  7. MTSC2019-深圳站 议题征集

    议题截止时间 11月初 议题投递地址 topic@testerhome.com   臣一路走来,没有敌人,看见的都是朋友和师长 —司马懿 关于中国移动互联网测试大会 MTSC 大会(中国移动互联网测试 ...

  8. 半导体行业MES系统应用案例

    半导体行业的发展是受惠在集成电路上的,但是收到技术瓶颈的阻碍,所以工业时期对半导体行业就造成了严重的冲击. 为了推动半导体行业快速发展,扭转像IBM.东芝以及富士康等IDM大厂利用晶圆代工对半导体制造 ...

  9. android中的webview白屏问题

     最近在使用WebView的时候,发现了一个小问题,很多初学者应该会注意不到! WebView的layerType属性有三个值. 1.none,默认值, 2.software,软件加速, 3.hard ...

  10. 一、DES加密和解密

    一.DES加密和解密 原文:http://www.jb51.net/article/51879.htm  还有其他文章 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 1 ...