Java多线程编程read-write模式

 首先我们来看一个read-write lock pattern的范例程序,该程序是一个多线程程序,用于对Data类的实例进行读取或者写入操作。
该程序需要实现的是当线程读取时,不允许写入线程更改实例的状态,但是此时允许新的线程读取实例的状态。而当写入线程工作时,不允许读线程读取实例的状态。

Main操作测试用的类
Data可读写的类
WriterThread写入的类
ReadThread读取操作的类
ReadWriteLock提供读写锁定的类
其类图如下所示


Data类的代码如下所示:
package cn.com.chanjet.readwritepattern;

/**
* Created by hujun on 2014/7/16.
*/
public class Data {
    private final char[] buffer;
    private final ReadWriteLock lock = new ReadWriteLock();

    public Data(int size) {
        this.buffer = new char[size];
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = '*';
        }
    }

    public void write(char c) throws InterruptedException {
//      获取锁定
        lock.writeLock();
        try {
            doWrite(c);
        } finally {
//      解除锁定
            lock.writeUnlock();
        }
    }

    public char[] read() throws InterruptedException{
//        获取锁定
        lock.readLock();
        try{
            return doRead();
        }finally {
//        解除锁定
            lock.readUnlock();
        }
    }

    private char[] doRead() {
        char[] newbuf=new char[buffer.length];
        for (int i = 0; i < buffer.length; i++) {
            newbuf[i]=buffer[i];
        }
        slowly();
        return newbuf;
    }

    private void doWrite(char c) {
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = c;
            slowly();
        }
    }

    private void slowly() {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

WriterThread类
package cn.com.chanjet.readwritepattern;

import java.util.Random;

/**
* Created by hujun on 2014/7/16.
*/
public class WriterThread extends Thread{
    private static final Random random=new Random();
    private final Data data;
    private final String filler;
    private int index=0;

    public WriterThread(Data data, String filler) {
        this.data = data;
        this.filler = filler;
    }
    public void run(){
        try {
            while (true){
                char c=nextchar();
                data.write(c);
                Thread.sleep(random.nextInt(3000));
            }
        }catch (InterruptedException e){
            System.out.println(e);
        }
    }
    private char nextchar() {
        char c=filler.charAt(index);
        index++;
        if(index >= filler.length()){
            index=0;
        }
        return c;
    }
}

ReaderThread类
package cn.com.chanjet.readwritepattern;

/**
* Created by hujun on 2014/7/16.
*/
public class ReaderThread extends  Thread{
    private final Data data;

    public ReaderThread(Data data) {
        this.data = data;
    }

    public void run(){
        try {
            while (true){
                char[] readbuf=data.read();
                System.out.println(Thread.currentThread().getName()+"reads "+String.valueOf(readbuf));
            }
        }catch (InterruptedException e){
            System.out.println(e);
        }
    }
}

ReadWriteLock类
package cn.com.chanjet.readwritepattern;

/**
* Created by hujun on 2014/7/16.
*/
public class ReadWriteLock {
    private int readingReaders=0;     //实例正在读取的线程数量
    private int waitingWriters=0;     //调用writeLock时进入wait状态的线程数量
    private int writingWriters=0;     //正在写入操作的线程数量,这个值不是0就是1
    private boolean perferWriter=true;  //用于标识写入优先还是读取优先


    public synchronized  void readLock() throws InterruptedException{
        //如果有写入进程正在工作,或者写入优先级高,此时有等待写入的进程工作时,读进程进入等待队列
        while (writingWriters>0||(perferWriter&&waitingWriters>0)){
            wait();
        }
        readingReaders++;
    }

    public synchronized void readUnlock() {
        readingReaders--;
        perferWriter=true;
        notifyAll();
    }

//    当有进程正在读取数据时,写进程阻塞
    public synchronized  void writeLock() throws  InterruptedException{
        waitingWriters++;
        try {
            while (readingReaders>0||writingWriters>0){
                wait();
            }
        }finally {
            waitingWriters--;
        }
        writingWriters++;
    }

    public synchronized void writeUnlock() {
       writingWriters--;
       perferWriter=false;
       notifyAll();

    }
}

该线程需要防止下面的两种冲突:
  • “读取”与“写入”的冲突
  • “写入”与"写入"的冲突
因此,需要考虑以下四种情况:
  • 当线程想要获取读访问锁定时:
    1. 当有线程正在写入时,等待
    2. 已经有线程正在读取时,不等待
  • 当线程想要获取写入用的锁定时:
  1. 已经有进程在写入时,等待
  2. 已经有进程正在读取时,等待
检验警戒条件:
readLock方法
    线程进行实际的读取操作之前会调用readLock方法,其警戒条件为:没有线程正在执行写入的操作,即writingWriter <=0,因此while语句的条件为警戒条件的否定,while(writingWriter>0).
    readLock方法退出之前其线程数据量readingReaders递增。
writeLock方法:
    线程进行写入操作前,会调用writeLock方法,其警戒条件为:没有线程正在执行读取或写入操作,即readingReaders<=0&&writingWriters>0,因此while语句的条件为警戒条件的否定,while(readingReaders>0||writingWriters>0)
    writeLock方法退出之前,需要将实际写入的线程数量writingWriters递增。

小结
在编写多线程程序的时候,最主要的是需要明确
1:多线程程序的参与者有哪些,这些参与者在多线程程序中扮演什么角色
2:多线程的共享资源是什么,其余各个参与者之间时什么关系
3:根据以上两点确定需要多线程程序中需要保护的到底是什么,在保护的过程如何避免数据不一致以及死锁情况的发生。
4:在确保程序正确的情况下,提高多线程程序的性能,需要注意的是此时的任何修改都应该是慎重的,因为此时对代码的修改会导致数据冲突情况的发生,有时宁愿牺牲一部分性能,也不要对多线程程序进行修改。


注:此文是对《Java多线程设计模式》中read-writer Lock Pattern的一个小结。大家如果对这部分内容感兴趣也可以直接阅读《Java多线程设计模式》这本书。


























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值