作者:凌杰林
简介
临界资源:同一时间只能由一个进程访问的资源
临界区:访问临界资源的代码段
读者写者问题:存在一个多个进程共享的数据区(临界资源),该数据区可以是一个文件或者一块内存空间,甚至可以是一组寄存器;有些进程reader只读取这个数据区的数据,有些进程writer只往数据区中写数据。此外,还需要满足以下条件:
1.任意数量的读进程可以同时读这个文件。
2.一次只能有一个写进程可以写这个文件。
3.若一个写进程正在写文件,则禁止任何读进程读文件。
4.读者优先:必须等所有读进程读完才可以写。
也即是说,读进程不排斥其它读进程,而写进程排斥其它所有进程,包括读进程和写进程。
分析
类的编写
Semaphore类
利用Java的多线程编程实现,操作系统中的p,v操作。p,v操作的函数添加synchronized关键字,加锁,确保同一时刻只有一个进程可以操作semValue这个临界资源,否则多个进程同时访问该值,会出现错误。
public class Semaphore{private int semValue;//定义信号量public Semaphore(int semValue) {this.semValue = semValue;}public synchronized void p() {semValue--;if (semValue < 0) {try {this.wait();//阻塞该进程} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void v(){semValue++;if (semValue <= 0) {this.notify();//唤醒被阻塞的进程}}
}
Reader类
定义读者类,实现Runnable接口,重写run方法。
class Reader implements Runnable{private Semaphore rmutex,wmutex;My my;static int readcount = 0;public Reader(Semaphore rmutex,Semaphore wmutex,My my){this.rmutex = rmutex;this.wmutex = wmutex;this.my = my;}@Overridepublic void run() {try{Thread.sleep((int)(1000*my.arrive_time));}catch (InterruptedException e){e.printStackTrace();}System.out.println("线程" + my.thread + "申请读操作");rmutex.p();if (readcount == 0) wmutex.p();readcount++;rmutex.v();System.out.println("线程" + my.thread + "开始读操作");try{Thread.sleep((int)(1000*my.operate_time));} catch (InterruptedException e){e.printStackTrace();}rmutex.p();readcount--;System.out.println("线程" + my.thread + "结束读操作");if (readcount == 0) wmutex.v();rmutex.v();}
}
细节
实现读者优先,定义readcount变量,初始值为0,用于记录当前读者进程的数量,第一个读者负责上锁,防止写进程申请资源,最有一个读进程负责解锁,唤醒被阻塞的写进程。
Writer类
定义写者类,实现Runnable接口,重写run方法。
class Writer implements Runnable{private Semaphore wmutex;My my;public Writer(Semaphore wmutex,My my){this.wmutex = wmutex;this.my = my;}@Overridepublic void run() {try{Thread.sleep((int)(1000*my.arrive_time));}catch (InterruptedException e){e.printStackTrace();}System.out.println("线程" + my.thread + "申请写操作");wmutex.p();System.out.println("线程" + my.thread + "开始写操作");try{Thread.sleep((int)(1000*my.operate_time));}catch (InterruptedException e){e.printStackTrace();}wmutex.v();System.out.println("线程" + my.thread + "完成写操作");}
}
其他类
该类定义了一个进程的编号,到达时间,读/写类型,要求服务时间。
class My
{int thread;String type;double arrive_time;double operate_time;My(String s){StringTokenizer st=new StringTokenizer(s);thread = Integer.parseInt(st.nextToken());type = st.nextToken();arrive_time = Double.parseDouble(st.nextToken());operate_time = Double.parseDouble(st.nextToken());}
该类是主类
import java.io.*;
import java.util.*;
public class ReaderWriter
{public static void main(String[] args) {Semaphore wmutex = new Semaphore(1);Semaphore rmutex = new Semaphore(1);int i;String s=null;My m[]=new My[10];try{BufferedReader br=new BufferedReader(new FileReader("Input.txt"));for (i=0;(s=br.readLine())!=null;i++) {m[i]=new My(s);System.out.println("线程"+m[i].thread+"是"+m[i].type+"线程,第"+m[i].arrive_time+"秒申请读写操作,操作持续"+m[i].operate_time+"秒");if (m[i].type.equals("R")){new Thread(new Reader(rmutex,wmutex,m[i])).start();}else {new Thread(new Writer(wmutex,m[i])).start();}}}catch(IOException e){} }
}
数据格式
1 R 1 5
2 W 3 4
数据存储在同目录下的input.txt文件中。
源代码
public class Semaphore{private int semValue;public Semaphore(int semValue) {this.semValue = semValue;}public synchronized void p() {semValue--;if (semValue < 0) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void v(){semValue++;if (semValue <= 0) {this.notify();}}
}//另外一个类
import java.io.*;
import java.util.*;
public class ReaderWriter
{public static void main(String[] args) {Semaphore wmutex = new Semaphore(1);Semaphore rmutex = new Semaphore(1);int i;String s=null;My m[]=new My[10];try{BufferedReader br=new BufferedReader(new FileReader("Input.txt"));for (i=0;(s=br.readLine())!=null;i++) {m[i]=new My(s);System.out.println("线程"+m[i].thread+"是"+m[i].type+"线程,第"+m[i].arrive_time+"秒申请读写操作,操作持续"+m[i].operate_time+"秒");if (m[i].type.equals("R")){new Thread(new Reader(rmutex,wmutex,m[i])).start();}else {new Thread(new Writer(wmutex,m[i])).start();}}}catch(IOException e){} }
}
class Reader implements Runnable{private Semaphore rmutex,wmutex;My my;static int readcount = 0;public Reader(Semaphore rmutex,Semaphore wmutex,My my){this.rmutex = rmutex;this.wmutex = wmutex;this.my = my;}@Overridepublic void run() {try{Thread.sleep((int)(1000*my.arrive_time));}catch (InterruptedException e){e.printStackTrace();}System.out.println("线程" + my.thread + "申请读操作");rmutex.p();if (readcount == 0) wmutex.p();readcount++;rmutex.v();System.out.println("线程" + my.thread + "开始读操作");try{Thread.sleep((int)(1000*my.operate_time));} catch (InterruptedException e){e.printStackTrace();}rmutex.p();readcount--;System.out.println("线程" + my.thread + "结束读操作");if (readcount == 0) wmutex.v();rmutex.v();}
}
class Writer implements Runnable{private Semaphore wmutex;My my;public Writer(Semaphore wmutex,My my){this.wmutex = wmutex;this.my = my;}@Overridepublic void run() {try{Thread.sleep((int)(1000*my.arrive_time));}catch (InterruptedException e){e.printStackTrace();}System.out.println("线程" + my.thread + "申请写操作");wmutex.p();System.out.println("线程" + my.thread + "开始写操作");try{Thread.sleep((int)(1000*my.operate_time));}catch (InterruptedException e){e.printStackTrace();}wmutex.v();System.out.println("线程" + my.thread + "完成写操作");}
}
class My
{int thread;String type;double arrive_time;double operate_time;My(String s){StringTokenizer st=new StringTokenizer(s);thread = Integer.parseInt(st.nextToken());type = st.nextToken();arrive_time = Double.parseDouble(st.nextToken());operate_time = Double.parseDouble(st.nextToken());}}
小结
读者-写者问题为我们解决互斥问题提供了一种参考思路,核心的思想时,在于设置一个计数器readcount记录当前正在访问共享文件的进程数目,用readcount的值来判断是否为最后一个或者最先一个读者进程,从而实现加锁或者解锁。另外,对readcount的检查和赋值操作要一气呵成,就是“原子性”,这时候,我们就应该想到要用操作系统课中的P,V操作来实现,设置好互斥的相关操作,构造好该信号量的类。