经典进程同步问题——读者写者问题
1. 问题描述
一个数据文件或者记录可被多个进程共享,我们把只要求读文件的进程称为“Reader”进程,其他进程则称为“Writer”进程。允许多个进程同时读一个共享对象,因为读操作不会使数据文件混乱。但是不允许一个Writer进程和其他的Reader进程或Writer进程同时访问共享对象(因为这种访问会引起数据的混乱)。
也就是读者-写者问题要求:
- 允许多个读者同时执行读操作;
- 不允许读者、写者同时操作;
- 不允许多个写者同时操作。
2. 问题分析
我们按照准备访问共享对象的进程种类来进行问题的分析:
如果Reader进程准备访问共享对象,当前系统中分为以下几种情况:
1)无Reader、Writer,这个新Reader可以读;
2)有Writer等,但有其它Reader正在读,则新Reader也可以读;
3)有Writer写,新Reader等待。
如果Writer进程准备访问共享对象,当前系统中分为以下几种情况:
1)无Reader、Writer,新Writer可以写;
2)有Reader,新Writer等待;
3)有其它Writer,新Writer等待。
3. 信号量设置
-
设置一个整型变量readcount表示正在读的进程数目,该变量是可被多个读进程访问的临界资源;
-
wmutex用于读者和写者、写者和写者进程之间的互斥;
-
rmutex用于对readcount这个临界资源的互斥访问。
4. 使用记录型信号量解决该问题——Java实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| package ReaderWriter;
import java.util.concurrent.Semaphore;
public class ReaderWriterTest1 {
static Semaphore rMutex = new Semaphore(1); static Semaphore wMutex = new Semaphore(1); static int readCount = 0;
static class Reader extends Thread { Reader(String name) { super.setName(name); }
@Override public void run() { while (true) { try { rMutex.acquire(); if(readCount == 0){ wMutex.acquire(); } readCount ++; rMutex.release(); System.out.println("读者【" + getName() + "】在执行读操作,当前读者数:【" + readCount + "】"); Thread.sleep(5000);
rMutex.acquire(); readCount --; if(readCount == 0){ wMutex.release(); } rMutex.release(); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
static class Writer extends Thread { Writer(String name) { super.setName(name); }
@Override public void run() { while (true) { try { wMutex.acquire(); System.out.println("写者【" + getName() + "】执行了写操作"); Thread.sleep(1000); wMutex.release(); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public static void main(String[] args) { Reader r1 = new Reader("r1"); Reader r2 = new Reader("r2"); Reader r3 = new Reader("r3");
Writer w1 = new Writer("w1"); Writer w2 = new Writer("w2");
r1.start(); r2.start(); r3.start(); w1.start(); w2.start(); } }
|
对于读进程中的if(readcount == 0) p(wmutex),这是因为读者和写者之间的关系决定的,因为读者到达且为当前时刻t1系统中的第一个读者,所以需要让写进程无法进入临界区。这里,还有一个精妙的设计,就是如果在t1时刻,已经有写者在操作共享对象,此时第一个读者来,去申请wmutex信号量,必定会因为资源不足而阻塞,这里通过一个wmutex来控制读者和写者的同步,可以说设计的非常精妙了。