并发系列(十二)-----ReentrantReadWriteLock的使用

一 简介

ReentrantReadWriteLock翻译过是读写锁的意思。并发编程中我们可能会遇到读多写少的情况,面对这种情况的时候我们可以使用ReentrantLock或者synchronized来保证数据的正确性。如果上述两种方法的话,这样的话不管是读操作还是写操作都要去获取锁和是释放锁,但是实际上我们可能只需要在写的时候保证线程线程是安全的,并且写完之后读线程得到的是修改后的数据,也就是说,在写的时候是独占的,不允许其他线程写也不允许其他线程读,而当线程读的时候,因为不会做修改的操作这时数据是可以共享的线程可以并行的访问。ReentrantReadWriteLock就可以很好的解决这种读多写少的问题。下面是两种的实现方式的差异

并发系列(十二)-----ReentrantReadWriteLock的使用_第1张图片二 使用

ReentrantReadWriteLock内部维护了两把锁,其中writeLock是独占锁也而readLock是共享锁。当使用构造方法初始化的ReentrantReadWriteLock时候内部就会初始化这两把锁,其中在使用构造方法的时候可以传一个布尔值,为true返回的两把公平锁,false为非公平锁,当不传的时候默认为fasle。下面是简单的使用代码

 /**
     * 初始化读写锁
     */
    private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    /**
     * 调用readLock()获取readLock
     */
    private static final Lock readLock = readWriteLock.readLock();
    /**
     * 调用writeLock()获取writeLock
     */
    private static final Lock writeLock = readWriteLock.writeLock();

    public static void main(String[] args) {
        //读的线程
        Runnable readThread = () -> {
            readData();
        };
        //写的线程
        Runnable writeThread = () -> {
            writeData();
        };
        for (int i = 0; i < 10; i++) {
            if (i%2 == 0){
                //偶数获取写锁
                Thread thread = new Thread(writeThread, "线程" + i);
                thread.start();
            }
            //其他线程都去获取读锁
            Thread thread = new Thread(readThread, "线程" + i);
            thread.start();
        }

    }

    public static void writeData() {
        writeLock.lock();//其他线程不能读也不能写
        try {
            //处理业务逻辑
            System.out.println(Thread.currentThread() + "获取到写锁");
            //写锁的时间设置长的的话可以观察它的独占性
            Thread.sleep(10000);
        } catch (Exception e){}finally {
            //没有正确的释放锁可能会导致死锁
            System.out.println(Thread.currentThread() + "释放写锁");
            writeLock.unlock();
        }
    }

    public static void readData() {
        readLock.lock();//其他线程不可写
        try {
            System.out.println(Thread.currentThread() + "获取到读锁");
            //读取数据
            Thread.sleep(100);
        }catch (Exception e){}finally {
            System.out.println(Thread.currentThread() + "释放读锁");
            readLock.unlock();
        }
    }

上面使用读写锁的时候是比较清晰的模式都是一种锁获取锁释放,然后另一个锁获取在释放,对于这种模式的话只要不要忘记去释放锁,一般都不会产生死锁,但在使用中还可能出现下面的模式。


    /**
     * 模式一 先获取写在获取读
     */
    public static void writeRead() {
        writeLock.lock();
        System.out.println("获取到写锁");
        readLock.lock();
        System.out.println("获取到读锁");
        readLock.unlock();
        System.out.println("释放读锁");
        writeLock.unlock();
        System.out.println("释放写锁");
    }
     /**
     * 模式二 先获取读在获取写
     */
    public static void readWrite() {
        readLock.lock();
        System.out.println("获取到读锁");
        writeLock.lock();
        System.out.println("获取到写锁");
        writeLock.unlock();
        System.out.println("释放写锁");
        readLock.unlock();
        System.out.println("释放读锁");

    }

上面两种模式中模式一是可以的,又叫锁降级,但模式二是不可以模式二会找成死锁。当然读写锁还一些API可以用来处理中断和超时和Reentrant相差不大。

三 总结

  1. 适合读多写少的场景。
  2. 利用共享模式实现读锁,独占模式实现写锁;
  3. 支持公平和非公平
  4. 写锁阻塞写锁和读锁,读锁阻塞写锁;

 

 

 

你可能感兴趣的:(并发,并发系列)