Java实现PV操作 | 读者与写者(在三种情况下进行讨论)
注 :本文应结合【天勤笔记】进行学习。
1.读者优先
设置rmutex信号量来对readcount变量进行互斥访问、mutex信号量对写者与读者进行同步。
static syn rmutex=new syn(1);//多个【读者】对readcount进行【互斥】访问
static syn mutex=new syn(1);//多个【写者】对数据区进行【互斥】访问
java代码:(点击加号可查看)
package 读者优先;
import java.util.Scanner; public class Main { public static void main(String[] args) {
System.out.print("请设置读者数目:");
Scanner scan=new Scanner(System.in);
int readNum =scan.nextInt(); System.out.print("请设置写者数目:");
scan=new Scanner(System.in);
int writeNum =scan.nextInt(); System.out.print("请设置循环上限:");
scan=new Scanner(System.in);
Global.UpBound =scan.nextInt(); scan.close(); Reader r[]=new Reader[readNum];
Writer w[]=new Writer[writeNum];
int i;
for(i=0;i<readNum;i++){
r[i]=new Reader(i+1);
}
for(i=0;i<writeNum;i++){
w[i]=new Writer(i+1);
}
Thread []r_t=new Thread[readNum];
Thread []w_t=new Thread[writeNum];
for(i=0;i<readNum;i++){
r_t[i]=new Thread(r[i]);
}
for(i=0;i<writeNum;i++){
w_t[i]=new Thread(w[i]);
}
for(i=0;i<writeNum;i++){
w_t[i].start();
}
for(i=0;i<readNum;i++){
r_t[i].start();
} } } 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
}
}
} class Global{
static syn rmutex=new syn(1);//多个【读者】对readcount进行【互斥】访问
static syn mutex=new syn(1);//多个【写者】对数据区进行【互斥】访问
static int dataZone=0; //数据区
static int readcount=0; //用于记录读者的数量
static int data=0;
static int UpBound=20;
} class Reader implements Runnable{//读者
int ID=0;
Reader(){}
Reader(int id){ID=id;}
public void run(){
while(Global.data<=Global.UpBound){
//对readcount进行操作
Global.rmutex.Wait();
if(Global.readcount==0){//这是第一个读者,应该阻止写者的进入
Global.mutex.Wait();
}
Global.readcount++;//读者数量增减
Global.rmutex.Signal();
//对readcount操作结束 /*
* 进行读操作
*/
int readData=Global.dataZone;
System.out.println("读者"+ID+"读出了数据:"+readData);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* 结束读操作
*/ //对readcount进行操作
Global.rmutex.Wait();
Global.readcount--;//读者数量减少
if(Global.readcount==0){//这是最后一个读者,唤醒写者
Global.mutex.Signal();
}
Global.rmutex.Signal();
//对readcount操作结束
}
}
} class Writer implements Runnable{//写者
int ID=0;
Writer(){}
Writer(int id){ID=id;}
public void run(){
while(Global.data<=Global.UpBound){
Global.mutex.Wait(); //申请对数据区进行访问
/*
* 进行写操作
*/
Global.data++;
int writeData=Global.data;
System.out.println("写者"+ID+"写入了数据:"+writeData);
Global.dataZone=Global.data;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* 结束写操作
*/
Global.mutex.Signal(); //释放数据区,允许其他进程读写
}
}
}
2.公平策略
在这里,增加wmutex信号量来表示是否有正在进行或等待的写者:
static syn wmutex=new syn(1);//表示是否存在【进行】或【等待】的【写者】
在读者readcount进入区和离开区增加wait(wmutex)和signal(wmutex)的操作:
在写者的进入区与离开区增加wait(wmutex)和signal(wmutex)的操作:
java代码:(点击加号可查看)
package 公平策略; import java.util.Scanner; public class Main { public static void main(String[] args) {
System.out.print("请设置读者数目:");
Scanner scan=new Scanner(System.in);
int readNum =scan.nextInt(); System.out.print("请设置写者数目:");
scan=new Scanner(System.in);
int writeNum =scan.nextInt(); System.out.print("请设置循环上限:");
scan=new Scanner(System.in);
Global.UpBound =scan.nextInt(); scan.close(); Reader r[]=new Reader[readNum];
Writer w[]=new Writer[writeNum];
int i;
for(i=0;i<readNum;i++){
r[i]=new Reader(i+1);
}
for(i=0;i<writeNum;i++){
w[i]=new Writer(i+1);
}
Thread []r_t=new Thread[readNum];
Thread []w_t=new Thread[writeNum];
for(i=0;i<readNum;i++){
r_t[i]=new Thread(r[i]);
}
for(i=0;i<writeNum;i++){
w_t[i]=new Thread(w[i]);
}
for(i=0;i<writeNum;i++){
w_t[i].start();
}
for(i=0;i<readNum;i++){
r_t[i].start();
} } } 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
}
}
} class Global{
static syn rmutex=new syn(1);//多个【读者】对readcount进行【互斥】访问
static syn mutex=new syn(1);//多个【写者】对数据区进行【互斥】访问
static int dataZone=0; //数据区
static int readcount=0; //用于记录读者的数量
static int data=0;
static int UpBound=20;
static syn wmutex=new syn(1);//表示是否存在【进行】或【等待】的【写者】
} class Reader implements Runnable{//读者
int ID=0;
Reader(){}
Reader(int id){ID=id;}
public void run(){
while(Global.data<=Global.UpBound){
Global.wmutex.Wait();//检测是否存在写者,无写者才能进入 //对readcount进行操作
Global.rmutex.Wait();
if(Global.readcount==0){//这是第一个读者,应该阻止写者的进入
Global.mutex.Wait();
}
Global.readcount++;//读者数量增减
Global.rmutex.Signal();
//对readcount操作结束 Global.wmutex.Signal();//恢复wmutex /*
* 进行读操作
*/
int readData=Global.dataZone;
System.out.println("读者"+ID+"读出了数据:"+readData);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* 结束读操作
*/ //对readcount进行操作
Global.rmutex.Wait();
Global.readcount--;//读者数量减少
if(Global.readcount==0){//这是最后一个读者,唤醒写者
Global.mutex.Signal();
}
Global.rmutex.Signal();
//对readcount操作结束 }
}
} class Writer implements Runnable{//写者
int ID=0;
Writer(){}
Writer(int id){ID=id;}
public void run(){
while(Global.data<=Global.UpBound){
Global.wmutex.Wait();
Global.mutex.Wait(); //申请对数据区进行访问
/*
* 进行写操作
*/
Global.data++;
int writeData=Global.data;
System.out.println("写者"+ID+"写入了数据:"+writeData);
Global.dataZone=Global.data;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* 结束写操作
*/
Global.mutex.Signal(); //释放数据区,允许其他进程读写
Global.wmutex.Signal();
}
}
}
3.写者优先
公平策略是在读者优先的基础上进行修改,写者优先也是在公平策略的基础上进行修改。
在这里,我们增加了readable信号量,writecount全局变量。
在读者中,用readable代替了【公平策略】中的wmutex来对等待队列中的写者进行标记:
在写者中,通过判断等待队列中是否有写者,来控制读者的进入,并用wmutex对writecount全局变量进行互斥访问:
java代码:(点击加号可查看)
package 写者优先; import java.util.Scanner; public class Main { public static void main(String[] args) {
System.out.print("请设置读者数目:");
Scanner scan=new Scanner(System.in);
int readNum =scan.nextInt(); System.out.print("请设置写者数目:");
scan=new Scanner(System.in);
int writeNum =scan.nextInt(); System.out.print("请设置循环上限:");
scan=new Scanner(System.in);
Global.UpBound =scan.nextInt(); scan.close(); Reader r[]=new Reader[readNum];
Writer w[]=new Writer[writeNum];
int i;
for(i=0;i<readNum;i++){
r[i]=new Reader(i+1);
}
for(i=0;i<writeNum;i++){
w[i]=new Writer(i+1);
}
Thread []r_t=new Thread[readNum];
Thread []w_t=new Thread[writeNum];
for(i=0;i<readNum;i++){
r_t[i]=new Thread(r[i]);
}
for(i=0;i<writeNum;i++){
w_t[i]=new Thread(w[i]);
}
for(i=0;i<writeNum;i++){
w_t[i].start();
}
for(i=0;i<readNum;i++){
r_t[i].start();
} } } 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
}
}
} class Global{
static syn mutex=new syn(1);//控制互斥访问的数据区
static syn rmutex=new syn(1);//多个【读者】对readcount进行【互斥】访问
static syn wmutex=new syn(1);//多个【写者】对writecount进行【互斥】访问
static syn readable=new syn(1);//表示当前是否有写者 static int dataZone=0; //数据区
static int readcount=0; //用于记录读者的数量
static int writecount=0; //用于记录读者的数量 static int data=0;
static int UpBound=20; } class Reader implements Runnable{//读者
int ID=0;
Reader(){}
Reader(int id){ID=id;}
public void run(){
while(Global.data<=Global.UpBound){
Global.readable.Wait();//检测是否存在写者,无写者才能进入 //对readcount进行操作
Global.rmutex.Wait();
if(Global.readcount==0){//这是第一个读者,应该阻止写者的进入
Global.mutex.Wait();
}
Global.readcount++;//读者数量增减
Global.rmutex.Signal();
//对readcount操作结束 Global.readable.Signal();//恢复readable /*
* 进行读操作
*/
int readData=Global.dataZone;
System.out.println("读者"+ID+"读出了数据:"+readData);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* 结束读操作
*/ //对readcount进行操作
Global.rmutex.Wait();
Global.readcount--;//读者数量减少
if(Global.readcount==0){//这是最后一个读者,唤醒写者
Global.mutex.Signal();
}
Global.rmutex.Signal();
//对readcount操作结束
}
}
} class Writer implements Runnable{//写者
int ID=0;
Writer(){}
Writer(int id){ID=id;}
public void run(){
while(Global.data<=Global.UpBound){
Global.wmutex.Wait();//准备修改writecount
if(Global.writecount==0) Global.readable.Wait();//如果是第一个读者,则阻止后续读者进入
Global.writecount++;
Global.wmutex.Signal();//结束对writecount的修改 Global.mutex.Wait(); //申请对数据区进行访问
/*
* 进行写操作
*/
Global.data++;
int writeData=Global.data;
System.out.println("写者"+ID+"写入了数据:"+writeData);
Global.dataZone=Global.data;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* 结束写操作
*/
Global.mutex.Signal(); //释放数据区,允许其他进程读写 Global.wmutex.Wait();//准备修改writecount
Global.writecount--;
if(Global.writecount==0) Global.readable.Signal();//如果是最后一个写者,唤醒读者
Global.wmutex.Signal();//结束对writecount的修改
}
}
}
4.三种情况下运行结果的对比
在同一组测试数据下,三种情况的运行结果见上图。左为读者优先,中为公平策略,右为写者优先。可见左图读者进行了大量的插队操作,中图的读者与写者都是交替进行的,右图的写者从一开始就在插队。
Java实现PV操作 | 读者与写者(在三种情况下进行讨论)的更多相关文章
- Java实现PV操作 | 生产者与消费者
导语 在学习操作系统的过程中,PV操作是很重要的一个环节.然而面对书本上枯燥的代码,每一个爱好技术的人总是想能亲自去实现.现在我要推出一个专题,专门讲述如何用Java实现PV操作,让操作系统背后的逻辑 ...
- JAVA写JSON的三种方法,java对象转json数据
JAVA写JSON的三种方法,java对象转json数据 转自:http://www.xdx97.com/#/single?bid=5afe2ff9-8cd1-67cf-e7bc-437b74c07a ...
- Node.js写文件的三种方法
Node.js写文件的三种方式: 1.通过管道流写文件 采用管道传输二进制流,可以实现自动管理流,可写流不必当心可读流流的过快而崩溃,适合大小文件传输(推荐) var readStream = fs. ...
- java用POI操作excel——随便写一下,最基础的东西
前两天部门实施在做一个东西,需要把客户放在Excel中的数据导入到Oracle数据库中,我就想着直接写一个模板,必要的时候改一下实体类应该可以解放实施同事的双手,不过在实际写的过程中,还是碰到很多问题 ...
- Java实现PV操作 | 哲学家进餐问题
运行结果: Java代码: public class Main { public static void main(String[] args) { Global global=new Global( ...
- Java中OutOfMemoryError(内存溢出)的三种情况及解决办法
转载自:http://blog.sina.com.cn/s/blog_701c951f0100n1sp.html 相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题, ...
- python面对对象编程------3:写集合类的三种方法
写一个集合类的三种方法:wrap,extend,invent 一:包装一个集合类 class Deck: def __init__( self ): self._cards = [card6(r+1, ...
- java代码中init method和destroy method的三种使用方式
在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. ...
- Java中数组转为List三种情况的优劣对比,常犯的类型转换错误原因解析
一.最常见方式(未必最佳)通过 Arrays.asList(strArray) 方式,将数组转换List后,不能对List增删,只能查改,否则抛异常. 关键代码:List list = Arrays. ...
随机推荐
- SQL Server的常用提示
在SQL Server中,有许多SQL语句的提示,本文总结一些比较常用的提示. OPTION LOOP/MERGE/HASH JOIN提示 该提示可以改变整个SQL语句中所有JOIN的关联算法,所以请 ...
- lazyload的使用方法
http://blog.csdn.net/peidandan/article/details/8107634
- 树莓派安装使用docker
2019/11/11, 树莓派4B, Raspbian Buster,Docker 19.03.4 摘要:树莓派Raspbian Buster中安装Docker,Dockerfile更改软件源 安装d ...
- tf.random_shuffle()函数解析
tf.random_shuffle(value, seed=None, name=None) 函数就是随机地将张量沿第一维度打乱 value:将被打乱的张量. seed:一个 Python 整数.用于 ...
- python自动备份阿里云数据库binlog
#coding:utf8from aliyunsdkcore import clientfrom aliyunsdkrds.request.v20140815 import DescribeBacku ...
- 深圳宝安图书馆官网错误 HTTP Status 500 - Servlet.init() for servlet spring threw exception
停留了一段时间没有动 打开https://www.balib.cn/balib/category/152 *********************************************** ...
- Windows中常用工具
护眼软件 f.lux https://justgetflux.com/ Typora https://www.typora.io/ Markdown工具,小巧,方便. Snipaste https:/ ...
- 封装axios,带请求头和响应头
import axios from "axios"; import qs from "qs"; //处理参数 import router from '../ro ...
- 英语partschinite芬达石partschinite锰铝榴石
partschinite指锰铝榴石,属于石榴石的一种,由于它有芬达饮料的诱人颜色,大家也称其为“芬达石”.石榴石宝石族中重要品种之一,化学成分为锰铝硅酸盐,颜色从红到橙红,红到棕红,玫瑰红.浅玫红均有 ...
- 【案例】保健品行业如何优化供应链管理?APS系统来帮忙
仙乐健康一直致力于为了客户提供世界级的产品及服务,随着业务量的不断扩大,公司先后实施了ERP系统,CRM系统,WMS系统,OA系统,朝着行业信息化水平领先的目标迈进. 但近年仅仅拥有传统ERP系统和手 ...