ReentrantReadWriteLock中读锁加锁原理

基本底层方法调用逻辑

当我们进入读锁的lock方法:

/**
         * Acquires the read lock.
         *
         * <p>Acquires the read lock if the write lock is not held by
         * another thread and returns immediately.
         *
         * <p>If the write lock is held by another thread then
         * the current thread becomes disabled for thread scheduling
         * purposes and lies dormant until the read lock has been acquired.
         */
        public void lock() {
            sync.acquireShared(1);
        }

我们继续进入acquireShared方法:

/**
     * Acquires in shared mode, ignoring interrupts.  Implemented by
     * first invoking at least once {@link #tryAcquireShared},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquireShared} until success.
     *
     * @param arg the acquire argument.  This value is conveyed to
     *        {@link #tryAcquireShared} but is otherwise uninterpreted
     *        and can represent anything you like.
     */
    public final void acquireShared(int arg) {
    	//尝试加锁
        if (tryAcquireShared(arg) < 0)
        	//加锁失败 当前线程加入队列之前 新建一个SHARED节点 node
        	//如果node节点的前一个节点为队列中的头结点 当前线程再次尝试加锁
        	//链式唤醒等待队列中等待线程 根据下一个节点的nextWaiter类型是否为SHARED
        	//SHARED代表读 读读并发 会一次唤醒SHARED节点
            doAcquireShared(arg);
    }

tryAcquireShared方法:尝试加读锁,如果加锁成功返回大于0的数(返回多少取决于加了几次读锁)。加锁失败返回-1。

可以看出具体的加读锁逻辑在tryAcquireShared方法内部,现在具体分析一下tryAcquireShared方法的实现逻辑。

1.tryAcquireShared方法实现逻辑

firstReader :第一个获得读锁的线程,更准确地说,firstReader 是最后一次将共享计数从 0 更改为 1 的唯一线程,此后一直没有释放读锁; 如果没有这样的线程,则为 null。

firstReaderHoldCount : firstReaderHoldCount 是 firstReader 的保留计数。就是该线程加了几次读锁。

readHolds:当前线程持有的可重入读锁的数量

cachedHoldCounter:成功获取 readLock 的最后一个线程的保持计数。保存每个线程读取保持计数的计数器。

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();
            //得到锁状态
            int c = getState();
            //exclusiveCount(c) != 0代表加了写锁
            //getExclusiveOwnerThread() != current 判断当前线程是否等于锁持有线程
            //如果加了写锁同时当前线程不等于锁持有线程就会加锁失败,直接返回-1
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            //如果上面代码没有返回-1,说明 此时有两种情况
            //1.没有加写锁
            //2.当前线程等于锁持有线程,造成锁的重入降级
            int r = sharedCount(c);//得到读锁的上锁次数
            //readerShouldBlock()判断当前线程是否应该睡眠
            //r读锁的上锁次数是否小于65535
            //cas读锁次数加1
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                //当执行到这里 可以说当前线程已经加读锁成功 为什么还会有下面一大堆代码
				//这里主要是为了性能的考虑 用局部变量缓存了第一次和最后一次加锁的信息 不用去线程的ThreadLocalMap去拿值
				//r==0 代表没有一个线程获取过读锁 这个r是当前线程加读锁成功之前的值
                if (r == 0) {
                	//把缓存第一次加锁的信息设置为当前线程
                    firstReader = current;
                    //firstReader 的保留计数设置为1
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) { //如果当前加锁线程等于缓存的第一次加锁的线程
                    //firstReader 的保留计数加1 这种情况是发生在锁的重入
                    firstReaderHoldCount++;
                } else {
                	//当代码执行else这里 说明 不是第一次加读锁同时当前加锁线程不等于缓存的第一次加锁的线程
                	//cachedHoldCounter得到最后一个加读锁的线程信息 默认为null
                    HoldCounter rh = cachedHoldCounter;
                    //如果最后一个加读锁的线程信息为null 
                    //或者 最后一个加读锁的线程信息中线程id不等于当前线程
                    if (rh == null || rh.tid != getThreadId(current))
                    	//把当前线程的信息 作为最后一个加读锁的线程信息
                    	//把当前信息存缓存一份 本地ThreadLocalMap一份
                        cachedHoldCounter = rh = readHolds.get();
                    //当最后一个加读锁的线程信息不为null
                    //同时最后一个加读锁的线程信息中线程id等于当前线程
                    //同时最后一个加读锁的线程信息中线程读锁计数为0
                    else if (rh.count == 0)
                    	//把当前线程本地ThreadLocalMap的值更新为当前线程加锁后的信息
                        readHolds.set(rh);
                    //更新缓存 把最后一个加读锁的线程信息中线程读锁计数加1
                    //用的指针 同时更新了缓存 和本地ThreadLocalMap的count值
                    rh.count++;
                }
                //返回1 加锁成功
                return 1;
            }
            return fullTryAcquireShared(current);
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值