读写锁分离-设计模式

Posted by 麦子 on Wednesday, 2019年12月04日

[TOC]

转载地址: https://blog.csdn.net/weixin_39715061/article/details/81081008

需求简明分析

  1. 读的时候有好几个线程一起去读它 并行的去读,不会对下一个产生问题。
  2. 如果有人在写 你就不能读了 线程就会wait住 ,如果有人在读,就不能写,暂停住。
  3. 也就是最终保证它能读的时候不写,写的时候不读,同时写的时候只能有一个线程在写:

ReadWriteLock

注意:这个读方法readLock,加入了synchronize关键字,但是他没有直接去读取数据, 只是为了判断在读的时候, 有没有线程对公共数据进行写的操作, 如果没有的话, 后面方法的读都是并发操作的,如果有的话,读就要wait模式了。

public class ReadWriteLock {
    // 定义变量
    private int readingReaders = 0;// 当前有几个线程进行读的操作
    private int waitingReaders = 0;// 有几个线程想读却读不了 方法放进waitset队列
    private int writingWriters = 0;// 当前有几个线程让它写
    private int waitingWriters = 0;// 在等着释放锁去写
    // 为了避免一直读 那么给它一个默认更喜欢
    private boolean preferWriter = true;

    public ReadWriteLock() {
        this(true);
    }

    public ReadWriteLock(boolean preferWriter) {
        this.preferWriter = preferWriter;
    }

    // 让它去读,
    public synchronized void readLock() throws InterruptedException {
        this.waitingReaders++;
        try {
            // 如果当前有线程在写 同时写的数多
            while (writingWriters > 0 || (preferWriter && waitingWriters > 0)) {
                // 你就不能读了
                System.out.println("有其他线程正在进行写或者等待写的操作,为保证数据的一致性,请稍后在读");
                this.wait();
            }
            // 如果它不写了,你就去读,代表当前有一个线程在读
            this.readingReaders++;
        } finally {
            // 等待读不等了,不在waitset里面了等待读的就少了一个
            this.waitingReaders--;
        }
    }

    // 放弃读的锁
    public synchronized void readUnlock() {
        this.readingReaders--; // 释放了一个read的锁
        this.notifyAll();
    }

    // 写的操作
    public synchronized void writeLock() throws InterruptedException {
        this.waitingWriters++;
        try {
            // 当有读的或者在写的
            while (readingReaders > 0 || writingWriters > 0) {
                // 就不能写了
                System.out.println("有其他线程正在进行读或者进行写的操作,为保证数据的一致性,请稍后在写");
                this.wait();
            }
            // 等带写的jia+ 有人在写
            this.writingWriters++;
        } finally {
            // 如果从阻塞状态退出
            this.waitingWriters--;
        }
    }

    public synchronized void writeUnlock() {
        this.writingWriters--;
        // 通知你其他可以去操作了
        this.notifyAll();
    }
}

SharedData 公共资源

public class SharedData {
    // 共享的数据读的操作不断从里面读,
    // 写的数据往里面填数据
    private char[] buffer;
    // 定义一个随机值,要去随机休眠的
    private final ReadWriteLock lock = new ReadWriteLock();

    public SharedData(int size) {
        // 初始化值
        this.buffer = new char[size];
        for (int i = 0; i < size; i++) {
            this.buffer[i] = '*';
        }
    }

    // 提供一个read方法 返回char[] 数据
    public char[] read() throws InterruptedException {
        try {// 获取read这个锁
            lock.readLock();
            // 读的方法
            return this.doRead();
        } finally {
            // 确保释放掉
            lock.readUnlock();
        }
    }

    // 写的操作
    public void write(char[] charData) throws InterruptedException {
        try {
            // 拿锁因为访问的资源是同一个
            lock.writeLock();
            // 去写东西
            this.doWrite(charData);
        } finally {
            // 释放掉这个锁
            lock.writeUnlock();
        }
    }

    private void doWrite(char[] charData) {
        this.buffer = charData;
    }

    private char[] doRead() {
        // 创建一个 副本 吧里面的数据读到newBuf里面
        char[] newBuf = new char[buffer.length];
        // 赋值
        for (int i = 0; i < buffer.length; i++)
            newBuf[i] = buffer[i];
        slowly(1_000);// 休眠一下 模拟读
        return newBuf;
    }

    private void slowly(int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
        }
    }
}

ReaderWorker


public class ReaderWorker extends Thread {
    // 共享的数据
    private final SharedData data;

    public ReaderWorker(SharedData 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) {
            e.printStackTrace();
        }
    }
}

ReadWriteLock

public class ReadWriteLock {
    // 定义变量
    private int readingReaders = 0;// 当前有几个线程进行读的操作
    private int waitingReaders = 0;// 有几个线程想读却读不了 方法放进waitset队列
    private int writingWriters = 0;// 当前有几个线程让它写
    private int waitingWriters = 0;// 在等着释放锁去写
    // 为了避免一直读 那么给它一个默认更喜欢
    private boolean preferWriter = true;

    public ReadWriteLock() {
        this(true);
    }

    public ReadWriteLock(boolean preferWriter) {
        this.preferWriter = preferWriter;
    }

    // 让它去读,
    // 注意这个读的锁并没有直接去读取数据, 只是为了判断在读的时候, 有没有线程对公共数据进行写的操作, 如果没有的话, 后面方法的读都是并发操作的。 
    public synchronized void readLock() throws InterruptedException {
        this.waitingReaders++;
        try {
            // 如果当前有线程在写 同时写的数多
            while (writingWriters > 0 || (preferWriter && waitingWriters > 0)) {
                // 你就不能读了
                System.out.println("有其他线程正在进行写或者等待写的操作,为保证数据的一致性,请稍后在读");
                this.wait();
            }
            // 如果它不写了,你就去读,代表当前有一个线程在读
            this.readingReaders++;
        } finally {
            // 等待读不等了,不在waitset里面了等待读的就少了一个
            this.waitingReaders--;
        }
    }

    // 放弃读的锁
    public synchronized void readUnlock() {
        this.readingReaders--; // 释放了一个read的锁
        this.notifyAll();
    }

    // 写的操作
    public synchronized void writeLock() throws InterruptedException {
        this.waitingWriters++;
        try {
            // 当有读的或者在写的
            while (readingReaders > 0 || writingWriters > 0) {
                // 就不能写了
                System.out.println("有其他线程正在进行读或者进行写的操作,为保证数据的一致性,请稍后在写");
                this.wait();
            }
            // 等带写的jia+ 有人在写
            this.writingWriters++;
        } finally {
            // 如果从阻塞状态退出
            this.waitingWriters--;
        }
    }

    public synchronized void writeUnlock() {
        this.writingWriters--;
        // 通知你其他可以去操作了
        this.notifyAll();
    }
}

ReadWritLockClient

public class ReadWritLockClient {
    public static void main(String[] args) {
        final SharedData sharedData = new SharedData(10);
        new ReaderWorker(sharedData).start();
        new ReaderWorker(sharedData).start();
        new ReaderWorker(sharedData).start();
        new ReaderWorker(sharedData).start();
        new ReaderWorker(sharedData).start();

        
        // new WriterWorker(sharedData, "qwertyuiopasdfg").start();
        new WriterWorker(sharedData, "maizi").start();
    }
}

运行结果

有其他线程正在进行读或者进行写的操作为保证数据的一致性请稍后在写
Thread-0 reads **********
有其他线程正在进行读或者进行写的操作为保证数据的一致性请稍后在写
Thread-2 reads **********
Thread-3 reads **********
Thread-1 reads **********
有其他线程正在进行写或者等待写的操作为保证数据的一致性请稍后在读
Thread-4 reads **********
有其他线程正在进行写或者等待写的操作为保证数据的一致性请稍后在读
有其他线程正在进行写或者等待写的操作为保证数据的一致性请稍后在读
有其他线程正在进行写或者等待写的操作为保证数据的一致性请稍后在读
有其他线程正在进行写或者等待写的操作为保证数据的一致性请稍后在读
I am write worker ......... Thread[Thread-5,5,main][C@39005f74
数据写入完毕
Thread-1 reads maizi
Thread-3 reads maizi

如上可以看到, 一开始有5条线程进行读的操作, 提示, 有人在读,写的操作需要wait, 而后读取完成后, 开始一条写的线程进入,开始写,这个时候5条线程需要等待写的操作完成,然后在可以开始读。 写完成后, 所有的就进行读取操作了。

需要注意里面的finally里面有很多的notifyAll的操作,这些都是为了防止线程中断时候,防止死锁处理的。 notityAll的唤醒主要是锁住同一个类的的时候, 线程和线程之间的通信的处理。

「真诚赞赏,手留余香」

真诚赞赏,手留余香

使用微信扫描二维码完成支付