ReentrantReadWriteLock之深入源码解析

简介

ReentrantLock是排他锁,在高并发读多写少的场景下,同时还要保证线程安全,如果使用ReentrantLock的效率不是那么好,所以才有了ReentrantReadWriteLock。

在讲源码之前,我们对读写锁要有个认知。
读读操作是共享的。
读写操作是互斥的。
写写操作是互斥的。
写读操作是互斥的。(写读两个不同线程)
单线程获取写锁后,再次获取读锁,可以拿到。(写读操作可以重入)
单线程获取读锁后,再次获取写锁,不可以拿到。(A线程拿到读锁,然后在获得写锁修改数据,由于读锁共享,这时B线程拿到读锁读取数据显然数据是有可能是脏数据)

使用方法

public class XxxTest {
    // 读写锁!
    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    // 写锁
    static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    // 读锁
    static ReentrantReadWriteLock.ReadLock readLock = lock.readLock();

    public static void main(String[] args) throws InterruptedException {
        readLock.lock();
        try {
            System.out.println("拿到读锁!");
        } finally {
            readLock.unlock();
        }

        writeLock.lock();
        try {
            System.out.println("拿到写锁!");
        } finally {
            writeLock.unlock();
        }
    }
}

源码解析

首先,ReentrantReadWriteLock也是基于AQS实现的,跟ReentrantLock类似,如果不了解,可以看下我的另一篇博客ReetrantLock源码分析,这样学习起来事半功倍。

跟ReetrantLock一样,是否拿到锁资源在于修改AQS的state的值,只不过在ReentrantReadWriteLock中,state的高16位是读锁,低16位是写锁。

从ReentrantReadWriteLock的构造方法看到默认是非公平锁
在这里插入图片描述

写锁加锁源码

在这里插入图片描述

来到AQS的acquire方法,除了tryAcquire方法,其它都跟ReentrantLock一样,我们直接看ReentrantReadWriteLock的tryAcquire方法
在这里插入图片描述

 protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
             //获取当前线程
            Thread current = Thread.currentThread();
           // 获取state
            int c = getState();
            //获取state低16位写锁
            int w = exclusiveCount(c);
          //  如果c不等于0,说明要么有读锁,要么有写锁
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                //如果有读锁 或者写锁不是自己 
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                    //进入这里,说明肯定是写锁而且锁的持有者是自己
                   //如果写锁重入次数溢出
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                获取锁成功
                setState(c + acquires);
                return true;
            }
            //c=0 锁无人持有
            //公平锁看下要不要排队writerShouldBlock->hasQueuedPredecessors()队列有排队返回true,否则反之
            //非公平锁开抢,writerShouldBlock直接返回false
            if (writerShouldBlock() ||
            	//走到这说明不用排队,直接进行CAS开抢
                !compareAndSetState(c, c + acquires))
                //CAS失败,返回false
                return false;
            //设置锁持有者为自己
            setExclusiveOwnerThread(current);
            return true;
        }

剩下的acquireQueued跟addWaiter都是AQS的方法,跟ReentrantLock一样。

写锁释放锁源码

在这里插入图片描述

其中unparkSuccessor方法跟ReentrantLock一样,我们直接看tryRelease方法
在这里插入图片描述

protected final boolean tryRelease(int releases) {
			//不是线程持有者释放锁,直接抛异常
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            //state - 1
            int nextc = getState() - releases;
            //判断低16位写锁是否为0
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
            	//线程持有者为null
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }

读锁加锁源码

在这里插入图片描述
在这里插入图片描述

 protected final int tryAcquireShared(int unused) {
            /*
             * Walkthrough:
             * 1. If write lock held by another thread, fail.
             * 2. Otherwise, this thread is eligible for
             *    lock wrt state, so ask if it should block
             *    because of queue policy. If not, try
             *    to grant by CASing state and updating count.
             *    Note that step does not check for reentrant
             *    acquires, which is postponed to full version
             *    to avoid having to check hold count in
             *    the more typical non-reentrant case.
             * 3. If step 2 fails either because thread
             *    apparently not eligible or CAS fails or count
             *    saturated, chain to version with full retry loop.
             */
             //获取当前线程
            Thread current = Thread.currentThread();
            //获取state
            int c = getState();
            //如果低16位不等于0说明有写锁 而且写锁持有者不是自己,去排队
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                
                return -1;
             //获取高16位读锁次数
            int r = sharedCount(c);
            //公平锁,readerShouldBlock如果有人排队返回ture
            //非公平锁,readerShouldBlock->apparentlyFirstQueuedIsExclusive->看完队列头的next是否是写锁在排队
            //因为读锁CAS是肯定成功的,如果读锁直接不顾排队的写锁直接CAS
            //那么当一直有读锁进来,写锁永远都在排队
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                //CAS对高16位加1
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                	使用firstReader存储第一个拿到读锁的线程
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                	//我是第一个拿到读锁线程的,现在进行锁重入
                    firstReaderHoldCount++;
                } else {
                	//不是第一个拿到读锁的
                	//获得最后一个读锁线程
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                    	//进来说明我是最后一个或者第二个来的,将自己赋值给cachedHoldCounter
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                    //之前来过,没拿过读锁,然后突然走了,现在再来,赋值为0
                        readHolds.set(rh);
                    //重入次数加1
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }
//进入这个方法说明在state低16位为0的情况下拿不到读锁
final int fullTryAcquireShared(Thread current) {
            /*
             * This code is in part redundant with that in
             * tryAcquireShared but is simpler overall by not
             * complicating tryAcquireShared with interactions between
             * retries and lazily reading hold counts.
             */
            HoldCounter rh = null;
            for (;;) {
                获取state
                int c = getState();
                //有写锁
                if (exclusiveCount(c) != 0) {
                   //不是自己
                    if (getExclusiveOwnerThread() != current)
                        return -1;
                    // else we hold the exclusive lock; blocking here
                    // would cause deadlock.
             //公平锁,readerShouldBlock如果有人排队返回ture
            //非公平锁,readerShouldBlock->apparentlyFirstQueuedIsExclusive->看完队列头的next是否是写锁在排队
            //因为读锁CAS是肯定成功的,如果读锁直接不顾排队的写锁直接CAS
            //那么当一直有读锁进来,写锁永远都在排队
            	//readerShouldBlock读锁应该阻塞,进入逻辑,说明是阻塞相关逻辑
                } else if (readerShouldBlock()) {
                    // Make sure we're not acquiring read lock reentrantly
                    if (firstReader == current) {
                        // assert firstReaderHoldCount > 0;
                    } else {
                        if (rh == null) {
                        //获取最后一个拿到读锁的
                            rh = cachedHoldCounter;
                            if (rh == null || rh.tid != getThreadId(current)) {
                            //获取自己重入次数
                                rh = readHolds.get();
                                //如果我的次数是0,说明不是重入操作
                                if (rh.count == 0)
                                //防止线程挂起内存泄漏
                                    readHolds.remove();
                            }
                        }
                        if (rh.count == 0)
                            return -1;
                    }
                }
                //超过读锁重入上限
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                 //CAS给state高16位加1
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                	//下面给tryAcquireShared一样
                    if (sharedCount(c) == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        if (rh == null)
                            rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                        cachedHoldCounter = rh; // cache for release
                    }
                    return 1;
                }
            }
        }

当tryAcquireShared返回-1时,执行doAcquireShared方法
在这里插入图片描述

如果当前读锁节点排队第一,尝试获取锁,否则挂起

private void doAcquireShared(int arg) {
		//添加共享锁节点到队列中
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            	//获取上一个节点
                final Node p = node.predecessor();
                if (p == head) {
                	//如果是头,尝试获取锁
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                    	//r大于=0,说明获取锁成功唤醒队列中要获取读锁的线程
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                
                //找到前面为-1的节点,才能执行挂起线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                   	//调用Unsafe类的park方法
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

读锁释放锁代码

在这里插入图片描述
在这里插入图片描述

protected final boolean tryReleaseShared(int unused) {
			获取当前线程
            Thread current = Thread.currentThread();
            如果第一个获得读锁的是当前线程
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                //如果重入次数为1
                if (firstReaderHoldCount == 1)
                	将第一个获得读锁线程置为空
                    firstReader = null;
                else
                	重入次数减1
                    firstReaderHoldCount--;
            } else {
            	获得最后一个读锁线程
                HoldCounter rh = cachedHoldCounter;
                //不是最后一个获得读锁线程
                if (rh == null || rh.tid != getThreadId(current))
                	获得自己的重入次数
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                	//防止内存泄漏
                    readHolds.remove();
                    if (count <= 0)
                    //抛异常
                        throw unmatchedUnlockException();
                }
                将锁重入次数减1
                --rh.count;
            }
            for (;;) {
            	获得state
                int c = getState();
                state高位减1
                int nextc = c - SHARED_UNIT;
               // CAS设置state
                if (compareAndSetState(c, nextc))
                    // Releasing the read lock has no effect on readers,
                    // but it may allow waiting writers to proceed if
                    // both read and write locks are now free.
                    return nextc == 0;
            }
        }

当tryReleaseShared返回true,此时state为0,既没有读锁也没有写锁获取到锁资源,调用doReleaseShared方法

private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                	//CAS将头节点状态从-1变为0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                        
                     //唤醒队列最前面状态为-1的节点
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                		//CAS将头节点从0变成-3
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

本人能力有限,如果有错,欢迎各位指出。

  • 46
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值