分析

读写锁最重要的需求是:多个线程如果都是在读取数据,如果依然采用加锁的方式,会严重影响效率。所以需要对读写锁进行分离

这种方式适用于:读取的操作比较多

需要考虑以下冲突,否则会出现数据不一致的情况

冲突 策略
读 — 读 并行化
读 — 写 串行化
写 — 写 串行化

读写锁的设计思想就是避免冲突

编码

首先设计的读写锁

  • 在获取读锁的时候,需要看看是否存在正在写的线程
  • 在获取写锁的时候,需要看看是否存在正在写的线程或者正在读的线程
  • 释放锁的就是在完成后,唤醒正在等待的全部线程
public class ReadWriteLock {

    private int readingReaders = 0;//当前有几个线程正在读
    private int waitingReaders = 0;//有几个线程想读,但是读取不了
    private int writeingWriters = 0;//正在写的线程数,最多一个
    private int waitingWriters = 0;//等待写的线程数

    /** * 读锁只需要关心是否有没有人在写 */

    public synchronized void readLock() {
        this.waitingReaders++;

        try {
            //如果此时有线程正在写,则不允许读的操作,陷入阻塞
            while (writeingWriters > 0) {
                this.wait();
            }
            //此时没有线程正在写
            this.readingReaders++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            waitingReaders--;
        }
    }

    /** * 释放读的锁 */
    public synchronized void readUnlock() {
        this.readingReaders--;
        this.notifyAll();
    }


    /** * 写锁 */
    public synchronized void writeLock() {
        this.waitingWriters++;

        try {
            //判断是否有线程正在读或者写
            while (readingReaders>0 || writeingWriters>0){
                this.wait();
            }
            writeingWriters++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            this.waitingWriters--;
        }
    }

    /** * 释放写锁 */
    public synchronized void wirteUnlock(){
        this.writeingWriters--;
        this.notifyAll();
    }

}

验证

设计两个类分别去读取数据和写入数据,然后创建客户端,创建多个写线程和读线程

共享的数据

/** * 共享的数据 */
public class ShareData {
    private final char [] buffer;

    private final ReadWriteLock lock = new ReadWriteLock();

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


    /** * 写的操作 * @param c 写的字符 */
    public void write(char c){
        try {
            lock.writeLock();
            doWrite(c);
        } finally {
            lock.wirteUnlock();
        }

    }

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

    public char[] read(){
        try {
            lock.readLock();
            return doRead();
        } finally {
            lock.readUnlock();
        }
    }

    /** * 读的操作,返回一个数据的副本 * @return */
    private char[] doRead() {
        char [] newBuf = new char[buffer.length];
        for (int i = 0; i < buffer.length; i++) {
            newBuf[i] = buffer[i];
        }
        slowly(50);
        return newBuf;
    }

    /** * 休眠 * @param time 休眠时间 */
    private void slowly(int time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

写的线程

public class WriteWorker extends Thread {
    private  final Random random = new Random(System.currentTimeMillis());

    private final ShareData data;
    private final String filer;

    private int index = 0;


    public WriteWorker(ShareData data, String filer) {
        this.data = data;
        this.filer = filer;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                char c = nextChar();
                data.write(c);
                Thread.sleep(random.nextInt(1000));
            }
        } catch (InterruptedException e) {
        }

    }

    /** * 获取一个字符,写到数据 * * @return */
    private char nextChar() {
        char c = filer.charAt(index);
        index++;
        if (index >= filer.length()) {
            index = 0;
        }
        return c;
    }
}

读取的线程

public class ReadWorker extends Thread {
    private final Random random = new Random();
    private final ShareData data;


    public ReadWorker(ShareData data) {
        this.data = data;
    }

    @Override
    public void run() {

        try {
            while (!Thread.interrupted()){
                char[] read = data.read();
                System.out.println(Thread.currentThread().getName() + " reads " + String.valueOf(read));
                Thread.sleep(random.nextInt(1000));
            }
        } catch (InterruptedException e) {

        }

    }
}

客户端

public class Client {
    public static void main(String[] args) throws InterruptedException {
        ShareData data = new ShareData(5);
        new ReadWorker(data).start();
        new ReadWorker(data).start();
        new ReadWorker(data).start();
        new ReadWorker(data).start();
        new ReadWorker(data).start();

        new WriteWorker(data,"uiiakihlkbjhao465117").start();
        new WriteWorker(data,"uiiakihlkbjhao465117").start();

        Thread.sleep(10_000);
        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
        threadGroup.interrupt();
    }
}

结果:

Thread-1 reads *****
Thread-2 reads *****
Thread-0 reads *****
Thread-3 reads *****
Thread-4 reads *****
Thread-3 reads uuuuu
Thread-4 reads uuuuu
Thread-3 reads uuuuu
Thread-0 reads uuuuu
Thread-3 reads uuuuu
Thread-2 reads iiiii
Thread-1 reads iiiii
Thread-3 reads kkkkk
Thread-1 reads kkkkk
Thread-4 reads kkkkk