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. ...
随机推荐
- C# - Array.Sort()方法
Array类简介 Array类是C#中所有数组的基类.我们常用的[]声明数组即为Array类的语法,我们可通过Array类提供的各种方法对C#中数组进行操作.最典型的就是数组排序 Array.Sort ...
- 论文笔记:LightGBM: A Highly Efficient Gradient Boosting Decision Tree
引言 GBDT已经有了比较成熟的应用,例如XGBoost和pGBRT,但是在特征维度很高数据量很大的时候依然不够快.一个主要的原因是,对于每个特征,他们都需要遍历每一条数据,对每一个可能的分割点去计算 ...
- 阿里云RDS数据库sql server 导入数据并添加作业小结
在阿里云购买ECS服务器和RDS数据库时,要注意网络类型要一致,最好都是VPC,否则ECS不能在内网访问RDS,只能从外网访问:在RDS控制台左侧,数据库安全性的IP白名单中添加ECS外网IP:在数据 ...
- 三分钟掌握,使用Quqrtz.Net实现定时发送邮件
在实际的项目中,常遇到延时触发工作以及定时触发工作 这里所讲的是借助第三方的组件 Quartz.Net 来实现(源码位置:https://github.com/quartznet/quartznet) ...
- Vertx与Spring配合完成DML操作
服务启动: public static void main( String[] args ) { ApplicationContext context = new AnnotationConfigAp ...
- ELK部署配置使用记录
为什么要用ELK: 一般我们需要进行日志分析场景:直接在日志文件中 grep.awk 就可以获得自己想要的信息.但在规模较大的场景中,此方法效率低下,面临问题包括日志量太大如何归档.文本搜索太慢怎么办 ...
- Typora使用手册
块状元素: 标题: # 标题1 ## 标题2 ### 标题3 #### 标题4 ##### 标题5 ###### 标题6 使用快捷键: Ctrl + 1-6 块引用 使用符号 > 就可以进入引用 ...
- 给任意view设置圆角outline.setRoundRect()方法实现
效果图,给banner设置圆角,1没有直接修改imageView而是使用自带方法进行设置 具体方法如下 //圆角方法 @TargetApi(Build.VERSION_CODES.LOLLIPOP) ...
- SAP技术 - How to create a CDS redirect view for a given database table
Scenario Suppose we have a database table A, and then we create a CDS redirect view B for it, then e ...
- Api测试-为postman自动添加cookie
使用postman来调试接口,会被buc-sso-csrf等拦截,需要自己挨个添加cookie,但是cookie又有失效时间,所以本篇介绍如何使用插件来自动获取cookie进行接口api测试 一.安装 ...