ReentrantReadWriteLock读写锁的介绍

510 阅读3分钟

本文正在参加「金石计划」

读写锁简单介绍

读写锁,顾名思义,包含两种锁,一种叫做读锁,一种叫做写锁。其中读锁是共享锁,写锁是排他锁。当持有读锁时,能够对共享资源进行读操作,持有写锁时,能够对共享资源进行写操作。读锁在同一时刻允许多个线程进行读操作获取锁资源,而写锁在同一时刻只允许一个线程获取到锁资源。

由于读写锁在同一时刻能够允许多个线程同时读取共享资源,而排它锁只允许一个线程获取到锁资源,所以在高并发的情况下,读写锁性能比排它锁要好一些。

读写锁源码讲解

读写锁是一个接口(ReadWriteLock),其位于java. util.concurrent.locks包下,ReadWriteLock接口中包含两个方法,分别为readLock()来获取读锁,writeLock()方法获取写锁,其源码如下:

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

ReadWriteLock读写锁接口的实现类是ReentrantReadWriteLock,在前面我们介绍过ReentranLock,他是一个支持多种方式获取锁的类,提到ReentranLock,我们在这里回顾一下Lock和synchronized的区别:

Lock和synchronized锁的区别:

synchronized本质上是关键字,而Lock锁是接口; synchronized可以作用于方法和代码块上,而Lock锁只能作用于代码块上; synchronized锁和Lock锁的底层不同,synchronized底层是基于objectMonitor对象锁来实现的,而Lock锁是基于AQS,FIFO先进先出队列实现的; synchronized只支持非公平锁,而Lock锁支持公平锁和非公平锁; synchronized是阻塞式加锁,而Lock是非阻塞式加锁,并且支持可中断式加锁,支持超时时间加锁; synchronized在加锁和解锁时,只有一个同步队列和一个等待队列,而Lock锁有一个同步队列和支持多个等待队列(condition); synchronized锁在进行等待和唤醒时,使用的是object类中的wait()和notify()方法,而lock锁使用的是condition接口的await()和signal()方法。

回归正题,我们来看一下ReentrantReadWriteLock实现类的源码:

    public ReentrantReadWriteLock() {
        this(false);
    }
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

ReentrantReadWriteLock支持公平锁和非公平锁的实现,可以根据fair变量使用三目运算符进行判断来创建公平锁和非公平锁,也可以使用默认创建方式,不传入参数直接创建非公平锁。

ReentrantReadWriteLock的内部实现同时依赖于内部类Sync,如下:

    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    final Sync sync;

在这里插入图片描述 我们点入Sync类查看源码可以看到,Sync是继承了AbstractQueuedSynchronizer的,因此ReentrantReadWriteLock实现类仍然依赖于AQS来实现。

在这里插入图片描述 在ReentrantReadWriteLock中实现读锁和写锁,需要维护一个读状态和写状态,在ReentrantReadWriteLock中,使用state高16位表示读状态(获取读锁的次数),使用state的低16位表示获取到写锁的线程的可重入的次数。

       static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        /** Returns the number of shared holds represented in count  */
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        /** Returns the number of exclusive holds represented in count  */
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

由于篇幅原因,本篇文章就先分享到这里了,后续会分享读写锁加锁和释放锁流程的知识,感谢大佬认真读完支持咯~ 、

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起讨论🍻 希望能和诸佬们一起努力,今后进入到心仪的公司 再次感谢各位小伙伴儿们的支持🤞

在这里插入图片描述