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. ...
随机推荐
- Elastic Stack 7.5.0白金版永不过期
适用版本:7.4.0~7.5.0 警告:本文章仅限于学习,非商业用途. 目录结构 # 先创建相关目录,具体结构如下: /opt |-- bulid # 编译目录 | |- src |-- instal ...
- VMwarm下安装ubuntu的一些问题
1.终端无法输入中文的原因(未实践) 原文地址 2.Windows10下VMwarm(V15.5)和ubuntu14.04实现复制文件(已经实践) 转载路径
- tkinter的set()与get()
下面用set()实现,这里用了一个标记记录点击的状态,不管内容是什么点了就变 from tkinter import * def btn_hit(): global msg_on if msg_on ...
- CENTOS7-JAVA模拟CPU占用高及排查( 转)
环境 centos7 1核2GB Java8 模拟cpu占用高 新建一个名为jvm-learn的springboot项目 模拟代码如下 import org.springframework.boot. ...
- 提高用git下载代码时的成功率
在用git clone下载一些比较大的仓库时,经常会遇到由于仓库体积过大,网络也不稳定,导致下了半截就中断了,可以参考如下的下载方法. 先用创建一个空目录,然后用git init初始化,然后用git ...
- SCP免密传输和SSH登录流程详解
SCP免密传输和SSH登录协议详解 在linux下开发时,经常需要登录到其他的设备上,例如虚拟机内ubuntu.树莓派等等,经常涉及到传输文件的操作,传输文件有很多中方法,如物理磁盘拷贝,基于网络的s ...
- lvs+keepalived高可用负载均衡
一.实验环境和网络拓扑图 本实验需要5台虚拟机,一台客户机,2台lvs调度器,两台web服务器. 客户机:192.168.0.6/24 lvs1:192.168.0.201/24 lvs2:192.1 ...
- scrum-master个人实践回顾总结
个人回顾总结 一.开课提出问题 第一次博客地址:https://www.cnblogs.com/Slow-Walker/p/11513179.html 二.问题回答 2.1问题1:针对单元测试 怎么保 ...
- 用Photoshop软件实现批量压缩照片
前提:手头有 "大" 照片,出于某种原因想把它变成 "小" 照片:电脑刚好安装有PS软件. 需知:如果您的压缩需求仅限于降低图片品质,降低图片像素,那么建议您采 ...
- php组合设计模式(composite pattern)
过十点. <?php /* The composite pattern is about treating the hierarchy of objects as a single object ...