22 多线程-读写锁分离

1 为什么读写分离

对于共享资源的读取是没有必要串行化,读取不会涉及对资源的修改,而写入操作是必须要保证线程安全的,
所以多线程下,如果读写分离,则可以提高效率,读数据是的是可以并行的,只有写的时候加锁串行

我们更多需求更多的时候就是读取的时候,允许多个线程去读取,但是在写如数据的时候,只能有一个线程去写入,其他线程不能进行读取和写入

2 实现

package study.wyy.concurrency.readWriteLock;

public class ReadWriteLock {
    /**
     * @Description: 当前正在读取数据线程数量
     */
    private int readingThreads = 0;

    /**
     * @Description: 等待去读取数据的线程的数量, 但是读取不了,放到了wait队列中
     */
    private int waitReadThreads = 0;

    /**
     * @Description: 正在写入数据的线程数量 只有一个
     */
    private int writingThreads = 0;

    /**
     * @Description: 记录当前有多少个线程想要写入,但是写入不了,放到了wait队列中,在等待其他释放锁
     */
    private int waitWriteThreads = 0;

    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 加读锁
     */
    public synchronized void readLock() throws InterruptedException {
        // 获取锁的时候,有可能获取不到锁,进入到等待队列(waitSet)中, 所以waitReadThreads可能需要加1的
        this.waitReadThreads++;
        try {
            while (writingThreads > 0) {
                // 当前有线程正在进行写入的时候,肯定是不能这个读取数据的线程肯定是不能进行读取数据的,进入等待
                // this.waitReadThreads++; 这个放在这里不合适,需要立马进入等待,就放到外面,但是可能会记录的数量不对
                this.wait();
            }
            // 此时没有线程进行写入数据,就可以读取数据了, 正在读取数据的线程数加1
            this.readingThreads++;
        } finally {
            // 最终释放锁的时候,要减减waitingReaders
            this.waitReadThreads--;
        }
    }

    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 释放读锁
     */
    public synchronized void readUnLock() throws InterruptedException {
        // 1 读取数据的线程的数量减1
        this.readingThreads--;
        // 2 释放
        this.notifyAll();
    }


    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 加写锁
     */
    public synchronized void writeLock() throws InterruptedException {
        // 获取锁的时候,有可能获取不到锁,进入到等待队列(waitSet)中, 所以waitWriteThreads可能需要加1的
        this.waitWriteThreads++;
        try {
            while (readingThreads > 0 || writingThreads > 0){
                // 当有其他线程在进行读取或者写入的时候,则当前线程不能进行写操作,只能等待
                this.wait();
            }
            // 没有其他线程在进行读取或者写入的时候,那就可以进行写入
            this.writingThreads++;
        } finally {
            // 最终释放锁的时候,要减减waitWriteThreads
            this.waitWriteThreads--;
        }
    }

    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 释放写锁
     */
    public synchronized void unWriteLock(){
        this.writingThreads--;
        this.notifyAll();
    }


}

3 测试

  • 定义数据
package study.wyy.concurrency.readWriteLock;

/**
 * @Description: 共享数据
 * @Author wyaoyao
 * @Date 2020/9/6 5:22 下午
 * @Param
 * @Return
 * @Exception
 */
public class ShareData {
    // 定义一个buffer,从里面读取数据和写入数据
    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++) {
            this.buffer[i] = '*';
        }
    }

    /**
     *  @author: wyaoyao
     *  @Date: 2020/9/6 5:27 下午
     *  @Description: 读取数据的方法
     */
    public  char[] read() throws InterruptedException {
        try {
            // 1 加读锁
            lock.readLock();
            // 2 读取数据
            return doRead();
        }finally {
            // 释放锁
            lock.readUnLock();
        }
    }

    private char[] doRead() {
        char[] temp = new char[buffer.length];
        for (int i =0; i<buffer.length;i++){
            temp[i] = buffer[i];
        }
        // 读取结束之后,休眠一会
        sleep(50);
        return temp;
    }

    private void sleep(int mills) {
        try {
            Thread.sleep(mills);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void write(char c) throws InterruptedException {
        try {
            // 1 加写锁
            lock.writeLock();
            // 2 写数据
            doWrite(c);
        }finally {
            // 释放锁
            lock.unWriteLock();
        }
    }

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

  • 定义读取数据的线程
package study.wyy.concurrency.readWriteLock;

/**
 * @Description : 数据写入线程
 * @Author  wyaoyao
 * @Date   2020/9/6 6:05 下午
 * @Param
 * @Return
 * @Exception
 */
public class ReadThread extends Thread{

    private final ShareData shareData;

    public ReadThread(ShareData shareData) {
        this.shareData = shareData;
    }

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

  • 定义写入数据的线程
package study.wyy.concurrency.readWriteLock;


import java.util.Random;

/**
* @Description : 数据读取线程
* @Author  wyaoyao
* @Date   2020/9/6 6:05 下午
* @Param
* @Return
* @Exception
*/
public class WriterThread extends Thread{

    private static final Random random = new Random(System.currentTimeMillis());

    private final ShareData shareData;

    // 写入内容
    private final String content;

    private int index = 0;

    public WriterThread(ShareData shareData, String content) {
        this.shareData = shareData;
        this.content = content;
    }

    /**
     *  @author: wyaoyao
     *  @Date: 2020/9/6 6:45 下午
     *  @Description:
     *   这么写有问题
     */
//    @Override
//    public void run() {
//        while (true){
//            char c = nextChar();
//            try {
//                shareData.write(c);
//                // 如果在里中断,下次醒过来还可能从这里进行
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
//    }

    @Override
    public void run() {
        // 在这里,如果中断,就会进入到catch,不用自己break,
        // 因为run方法已经结束了
        try{
            while (true){
                char c = nextChar();
                shareData.write(c);
                Thread.sleep(random.nextInt(1000));
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    /**
     *  @author: wyaoyao
     *  @Date: 2020/9/6 6:41 下午
     *  @Description: 获取content中的下一个字符
     */
    private char nextChar(){
        char c = content.charAt(index);
        index++;
        if(index>=content.length()){
            // 如果index大于content的长度,就重置一下
            index = 0;
        }
        return c;
    }
}

  • 测试类
package study.wyy.concurrency.readWriteLock;


/**
* @Description 测试
* @Author  wyaoyao
* @Date   2020/9/6 6:52 下午
* @Param
* @Return
* @Exception
*/
public class ReadWriteLockTest {

    public static void main(String[] args) {
        final ShareData shareData = new ShareData(10);
        new ReadThread(shareData).start();
        new ReadThread(shareData).start();
        new ReadThread(shareData).start();
        new ReadThread(shareData).start();
        new WriterThread(shareData,"qwertyuiop").start();
        new WriterThread(shareData,"QWERTYUIOP").start();
    }
}

运行结果发现,大部分是读取数据的线程抢到执行权,这也很正常,我们声明了4个读取数据的线程

4 改进

package study.wyy.concurrency.readWriteLock;

public class ReadWriteLock {
    /**
     * @Description: 当前正在读取数据线程数量
     */
    private int readingThreads = 0;

    /**
     * @Description: 等待去读取数据的线程的数量, 但是读取不了,放到了wait队列中
     */
    private int waitReadThreads = 0;

    /**
     * @Description: 正在写入数据的线程数量 只有一个
     */
    private int writingThreads = 0;

    /**
     * @Description: 记录当前有多少个线程想要写入,但是写入不了,放到了wait队列中,在等待其他释放锁
     */
    private int waitWriteThreads = 0;

    /**
     * @Description: 是否写入优先级更高
     */
    private boolean preferWrite;

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

    public ReadWriteLock() {
        // 默认 true
        this(true);
    }

    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 加读锁
     */
    public synchronized void readLock() throws InterruptedException {
        // 获取锁的时候,有可能获取不到锁,进入到等待队列(waitSet)中, 所以waitReadThreads可能需要加1的
        this.waitReadThreads++;
        try {
            while (writingThreads > 0 || (preferWrite && this.waitWriteThreads>0))  {
                // 当前有线程正在进行写入的时候,肯定是不能这个读取数据的线程肯定是不能进行读取数据的,进入等待
                // 如果更偏爱写入,并且有写入的线程数量大于0,也让读取的线程进入等待
                // this.waitReadThreads++; 这个放在这里不合适,需要立马进入等待,就放到外面,但是可能会记录的数量不对
                this.wait();
            }
            // 此时没有线程进行写入数据,就可以读取数据了, 正在读取数据的线程数加1
            this.readingThreads++;
        } finally {
            // 最终释放锁的时候,要减减waitingReaders
            this.waitReadThreads--;
        }
    }

    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 释放读锁
     */
    public synchronized void readUnLock() throws InterruptedException {
        // 1 读取数据的线程的数量减1
        this.readingThreads--;
        // 2 释放
        this.notifyAll();
    }


    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 加写锁
     */
    public synchronized void writeLock() throws InterruptedException {
        // 获取锁的时候,有可能获取不到锁,进入到等待队列(waitSet)中, 所以waitWriteThreads可能需要加1的
        this.waitWriteThreads++;
        try {
            while (readingThreads > 0 || writingThreads > 0){
                // 当有其他线程在进行读取或者写入的时候,则当前线程不能进行写操作,只能等待
                this.wait();
            }
            // 没有其他线程在进行读取或者写入的时候,那就可以进行写入
            this.writingThreads++;
        } finally {
            // 最终释放锁的时候,要减减waitWriteThreads
            this.waitWriteThreads--;
        }
    }

    /**
     * @author: wyaoyao
     * @Date: 2020/9/6 6:10 下午
     * @Description: 释放写锁
     */
    public synchronized void unWriteLock(){
        this.writingThreads--;
        this.notifyAll();
    }


}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值