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. ...
随机推荐
- NetCore 统一处理 webapi 返回null 转为“”
数据库中部分表字段允许空值,则代码中实体类对应的字段类型为可空类型Nullable<>,如int?,DateTime?,null值字段序列化返回的值都为null,前端对应字段赋值需要做nu ...
- mybatis映射mapper文件做like模糊查询
方法:使用concat函数连接通配符
- Java自学-异常处理 Exception
Java 异常 Exception 异常定义: 导致程序的正常流程被中断的事件,叫做异常 步骤 1 : 文件不存在异常 比如要打开d盘的LOL.exe文件,这个文件是有可能不存在的 Java中通过 n ...
- 【转载】C#通过InsertAt方法在DataTable特定位置插入一条数据
在C#中的Datatable数据变量的操作过程中,可以通过DataTable变量的Rows属性的InsertAt方法往DataTable的指定位置行数位置插入一个新行数据,即往DataTable表格指 ...
- SpringBoot,SSM和SSH
Springboot的概念: 是提供的全新框架,使用来简化Spring的初始搭建和开发过程,使用了特定的方式来进行配置,让开发人员不在需要定义样板化的配置.此框架不需要配置xml,依赖于想MAVEN这 ...
- Qt NetWork即时通讯网络聊天室(基于TCP)
本文使用QT的网络模块来创建一个网络聊天室程序,主要包括以下功能: 1.基于TCP的可靠连接(QTcpServer.QTcpSocket) 2.一个服务器,多个客户端 3.服务器接收到某个客户端的请求 ...
- 英语kutnahorite金田黄kutnahorite单词
金田黄是含镁和锰较多的方解石,学名是“镁锰方解石”或者“镁菱锰矿”,英文名为Kutnahorite,也可翻译为“锰白云石”.“金田黄”颜色艳丽,有红色.橙色或者黄色等不同色调,属于艳色方解石的集合体. ...
- 英文finaunce金融
金融 1.指货币的发行.流通和回笼,贷款的发放和收回,存款的存入和提取,汇兑的往来等经济活动. 胡适<国际的中国>:“我们更想想这几年国内的资产阶级,为了贪图高利债的利益,拚命的借债给中国 ...
- Android-----File(文件各种操作)
在Android编程中,操作文件是基本的必备技能,现在做一个操作文件的小工具:DocumentTool.java 代码如下: package com.hs.example.exampleapplica ...
- Miniconda
什么是conda conda的基本使用方式 conda是一个开源包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换. 它适用于Linux,OS X和Windows,是 ...