《图解Java多线程设计模式》之七:Read-Write Lock模式

一,Read-Write Lock模式

在Read-Write Lock模式中,读取操作和写入操作是分开考虑的。在执行读取操作之前,线程必须获取用于读取的锁。
在执行写入操作之前,线程必须获取用于写入的锁。所以:
当一个线程在读取时,其他线程可以读取,但是不可以写入。
当一个线程正在写入时,其他线程不可以读取或写入。
因为执行互斥处理会降低程序的性能,但是如果把写入的互斥处理和读取的互斥处理分开来考虑,就可以提高系统性能

二,示例程序

public final class ReadWriteLock {
    private int readingReaders = 0;//实际正在读取中的线程个数
    private int waitingWriters = 0;//正在等待写入的线程个数
    private int writingWriters = 0;//实际正在写入的线程个数
    private boolean preferWriter = true;//若写入优先,则为true

    public synchronized void readLock()throws InterruptedException{
        while (writingWriters > 0 || (preferWriter && waitingWriters > 0)){
            wait();
        }
        readingReaders++;//正在读取的线程个数加1
    }

    public synchronized void readUnlock(){
        readingReaders--;//正在读取的线程个数减1
        preferWriter = true;
        notifyAll();
    }

    public synchronized void writeLock()throws InterruptedException{
        waitingWriters++;//等待写入的线程加1
        try {
            while (readingReaders > 0 || writingWriters > 0){
                wait();
            }
        }finally {
            waitingWriters--;//等待写入的线程减1
        }
        writingWriters++;//正在写入的线程加1
    }

    public synchronized void writeUnlock(){
        writingWriters--;//正在写入的线程减1
        preferWriter = false;
        notifyAll();
    }
}
public class Data {
    private final char[] buffer;
    public 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 char[] read()throws InterruptedException{
        lock.readLock();
        try {
            return doRead();
        }finally {
            lock.readUnlock();
        }
    }

    /**
     * 实际的读取操作,创建一个新的char数组newbuf,来复制buffer里的内容,并返回newbuf
     * @return
     */
    private char[] doRead(){
        char[] newbuf = new char[buffer.length];
        for (int i = 0; i < buffer.length; i++) {
            newbuf[i] = buffer[i];
        }
        slowly();
        return newbuf;

    }

    public void write(char c)throws InterruptedException{
        lock.writeLock();
        try {
            doWrite(c);
        }finally {
            lock.writeUnlock();
        }
    }

    /**
     * 实际的写入操作,每读取一个字符,就执行一次耗时操作。写入操作时间比读取操作时间长
     * @param c
     */
    public void doWrite(char c){
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = c;
            slowly();
        }
    }

    /**
     * 耗时操作,模拟处理业务逻辑
     */
    public void slowly(){
        try {
            Thread.sleep(50);
        }catch (InterruptedException e){

        }
    }
}
public class ReaderThread extends Thread {
    private final Data data;
    public ReaderThread(Data data){
        this.data = data;
    }

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

        }
    }
}
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;
    }

    @Override
    public void run() {
        try {
            while (true){
                char c = nextchar();
                data.write(c);
                Thread.sleep(random.nextInt(3000));
            }
        }catch (InterruptedException e){

        }
    }

    private char nextchar(){
        char c = filler.charAt(index);
        index++;
        if (index >= filler.length()){
            index = 0;
        }
        return c;
    }
}
public class Test {
    public static void main(String[] args) {
        Data data = new Data(10);
        for (int i = 0; i < 6; i++) {
            new ReaderThread(data).start();
        }

        new WriterThread(data,"ABCDEFGHIJKLMNOPQRSTUVWXYZ").start();
        new WriterThread(data,"abcdefghijklmnopqrstuvwxyz").start();
    }
}

 三,适用场景

1.读取操作频繁
2.读取频率比写入频率高

四,锁的含义

1.synchronized可以用于获取实例的锁,Java的每一个实例都持有一个锁,但同一个锁不能被两个以上的线程同时获取,
这种结构是Java编程规范规定的,java虚拟机也是这样实现的。这就是java提供的物理锁。
2.本章的 用于读取的锁 和 用于写入的锁,是开发人员自己实现的一种结构。这种锁是逻辑锁,我们可以通过修改ReadWriteLock类来改变锁的运行
ReadWriteLock类提供了用于 读取的锁 和 用于写入的锁 的这两个逻辑锁。但是用于实现这两个逻辑锁的物理锁只有一个,就是ReadWriteLock实例持有的锁

五,java.util.concurrent.locks.ReadWriteLock

java并发包提供了读写锁。

public class Data {
    private final char[] buffer;
    private final ReadWriteLock lock = new ReentrantReadWriteLock(true);
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();



    public Data(int size){
        this.buffer = new char[size];
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = '*';
        }
    }
    public char[] read()throws InterruptedException{
       
        readLock.lock();
        try {
            return doRead();
        }finally {
       
            readLock.unlock();
        }
    }

    /**
     * 实际的读取操作,创建一个新的char数组newbuf,来复制buffer里的内容,并返回newbuf
     * @return
     */
    private char[] doRead(){
        char[] newbuf = new char[buffer.length];
        for (int i = 0; i < buffer.length; i++) {
            newbuf[i] = buffer[i];
        }
        slowly();
        return newbuf;

    }

    public void write(char c)throws InterruptedException{
      
        writeLock.lock();
        try {
            doWrite(c);
        }finally {
          
            writeLock.unlock();
        }
    }

    /**
     * 实际的写入操作,每读取一个字符,就执行一次耗时操作。写入操作时间比读取操作时间长
     * @param c
     */
    public void doWrite(char c){
        for (int i = 0; i < buffer.length; i++) {
            buffer[i] = c;
            slowly();
        }
    }

    /**
     * 耗时操作,模拟处理业务逻辑
     */
    public void slowly(){
        try {
            Thread.sleep(50);
        }catch (InterruptedException e){

        }
    }
}
原文地址:https://www.cnblogs.com/inspred/p/9398305.html