• 《图解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){
    
            }
        }
    }
  • 相关阅读:
    安卓巴士诚招版主,希望各位巴友踊跃加入我们!
    android用户界面之菜单(Menu)教程实例汇总
    360将推出多款360用户特供手机
    安卓巴士最新精选文章,请您查阅
    android用户界面之SeekBar教程实例汇总
    Android OpenGL ES 开发教程小结
    android用户界面之按钮(Button)教程实例汇
    Seleniumwebdriver系列教程(5)————如何定位frame中的元素
    Seleniumwebdriver系列教程(10)————如何智能的等待页面加载完成
    Seleniumwebdriver系列教程(6)————如何捕获弹出窗口
  • 原文地址:https://www.cnblogs.com/inspred/p/9398305.html
Copyright © 2020-2023  润新知