问题背景:一个数据文件或记录,可以被多个进程或者线程共享,其中不同的读进程可以并发的访问该临界资源,读和写进程或者线程,不能并发的对该临界资源进行操作,否则造成脏读和脏写,写和写进程之间也是互斥的。而我们今天的读者和写者模式 也称为读写锁,不过这里以信号量的方式进行互斥操作,就可以解决这个问题。
下面来看代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class RWTest {
//创建一个用于记录正在读的读进程数
static int readCount=0;
//创建一个读写线程共享的临界资源
static int sse=0;
public static void main(String[] args) {
//创建一个线程池
ExecutorService es=Executors.newFixedThreadPool(10);
//创建一个读写互斥的信号量
final Semaphore wmutex=new Semaphore(1,true);
//创建一个用于读进程间的修改临界资源readCount的互斥信号量
final Semaphore rmutex=new Semaphore(1,true);
//创建4个读进程
for(int i=0;i<4;i++){
es.execute(new Runnable(){
@Override
public void run() {
while(!Thread.interrupted()){
try {
//获取可以修改readCount的互斥信号量
rmutex.acquire();
//当readCount==0时说明,不存在读线程获得读写的互斥信号量,该信号量只对写线程操作互斥,对读线程不互斥
if(readCount==0){
//获取可以进行IO操作的的互斥信号
wmutex.acquire();
}
readCount++;
//释放信号量
rmutex.release();
//进行IO读操作
//这里我为了简单明了直接打印
System.out.println(Thread.currentThread().getId()+"正在进行读操作。。。。。。。。");
Thread.sleep(100);
System.out.println("读取的数据为"+sse);
//获取可以修改readCount的互斥信号量
rmutex.acquire();
readCount--;
//释放信号量
rmutex.release();
//当readCount==0时说明,所有读线程不再对共享资源进行操作了,则释放互斥信号量
if(readCount==0)
//释放信号量
wmutex.release();
//将cpu调度权让给其他读线程,但操作不一定成功
Thread.yield();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
//创建3个写线程
for(int i=0;i<3;i++){
es.execute(new Runnable(){
@Override
public void run() {
while(!Thread.interrupted()){
try {
//获取可以进行IO操作的互斥信号量,防止其他读写线程对临界资源进行操作,如果获取不到,则等待
wmutex.acquire();
//进行IO 写操作
//同样便于简单明了,这里只输出打印
System.out.println(Thread.currentThread().getId()+"正在进行写操作.........");
//修改临界资源的值
sse++;
//模拟IO操作所需时间
Thread.sleep(1000);
System.out.println("数据修改成功");
//释放信号量,让其他的读或写线程可以竞争
wmutex.release();
//将cpu的调度权让给其他写线程,但操作不一定成功
Thread.yield();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
}
}
}
结果分析:
我们代码中的无论读或写都不是原子操作的。
有上图可以看出读进程之间是不互斥的,可以并发访问。
有上图可以看出,读进程和写进程之间是互斥的,写进程和写进程之间也是互斥的。