ReentrantReadWriteLock实现原理

ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
       readLock.lock();
           sync.acquireShared(1);
               1.tryAcquireShared(arg)
                   1.如果写锁被其他线程持有,获取锁失败,
                   2.如果读锁不需要阻塞并且读锁的个数在最大值允许范围内,则尝试cas获取锁,
                   2.1 获取读锁成功,如果读锁次数为1,则将firstReader属性设置成前线程并设置firstReaderHolderCount为1
                   2.2 如果读锁次数r大于0并且当前线程等于firstReader则更新firstReaderHolderCount值加1
                   2.3 否则增加总的缓存的读锁次数
                   2.4操作完成返回1
                   3.2步骤没成功则执行fullTryAcquireShared().这个方法是TryAcquireShared()的完全版本,就是先尝试性能优化版的方式获取读锁,如果失败在执行完全版本的读锁获取方式
                   4.获取锁失败则执行下一步骤doAcquireShared(arg)
               2.doAcquireShared(arg);
                   1.addWaiter(Node.SHARED);
                       1.将当前线程封装成Node
                       2.如果tail和head未初始化,则初始化,并且都指向node
                       3.如果tail不为null,则将node插入到同步队列尾部
                   2.进入自旋.
                   3.检查当前节点的前置节点是否是head,
                       1.如果是则尝试tryAcquireShared(arg);方法见上.
                       2.如果获取锁成功则执行setHeadAndPropagate()
                       3.setHeadAndPropagate(node, r);
                           1.将当前节点设置为head节点,由于是重入锁(内部是共享锁)所以需要检查判断是否需要接续后续等待线程获取同步锁
                           2.如果propagate属性>0或者后续线程在共享锁模式中等待,则执行doReleaseShared();
                           3.doReleaseShared();
                               1.共享锁模式下的释放操作
                               2.在head不为null并且head不等于tail的情况下,如果head的waitstatus是Signal(后续节点等待激活状态).则cas设置head的状态为0并且unpark head节点的next节点
                               3.如果waitstatus为0(初始化状态),则将head的状态设置为PROPAGATE(代表共享等待模式,下一次获取共享锁无条件传播)
                               4.unpark后续节点线程
                       4.将原head节点的next指针设置为null(帮助gc 回收)
                       5.如果interrupted为true则触发selfInterrupt();
                       6.将failed设置为false
                       7.返回;
                   4.执行shouldParkAfterFailedAcquire(p, node)
                       1.如果状态已经是SIGNAL则直接返回
                       2.检查并在同步链中去掉已经CANCELLED的节点
                       3.将节点设置为SIGNAL状态等待被激活
                   5.上一步返回true则执行parkAndCheckInterrupt()
                       1.park当前线程
                       2.返回线程的interrupted状态
                   6.如果第五步返回true则将interrupted设置为true
                   7.在finally块中将failed等于true的节点执行 cancelAcquire(node);.这里唯一可能执行的情况是上面的selfInterrupt()触发的情况;
                       1.node=null直接返回不做处理
                       2.node的thread属性设置为null
                       3.检测node的pre节点并且移除出于CANCELLED状态的节点,
                       4.将node节点的状态设置为CANCELLED状态
                       5.如果node是tail节点.则cas更新tail为pre节点,并将pred 的next节点设置为null(因为是tail了不能有next节点)
                       6.如果不是tail节点也不是head节点,则cas移除同步链中的node节点
                       7.如果是head则执行unparkSuccessor(node),
                       8.将node.next设置为自己,帮助gc回收
       readLock.unlock();
            sync.releaseShared(1);
               1.先执行tryReleaseShared(arg)
                   1.如果firstReader属性等于当前线程
                       1.如果firstReaderHoldCount==1,则如果firstReader设置为null,因为等于1在释放一次锁之后就不在占有锁了
                       2.否则执行firstReaderHoldCount--;
                   2.firstReader不等于当前线程
                       1.拿到cachedHoldCounter值,这是将threadLocal变量以线程id为key的缓存值
                       2.如果cachedHoldCounter为空,或者cachedHoldCounter不是当前线程的值,则重新通过readHolds.get()获取.
                       3.拿到HoldCounter中的count值,
                           1.如果小于1则移除readHolds中当前现成的值
                           2.如果count小于0,抛出异常.
                       4.执行--count操作
                   3.进入自旋
                       1.拿到锁状态属性state
                       2.计算锁的新值,并使用cas更新
                       3.返回新值==0  代表是否已经释放锁
               2.如果第一步返回true则执行doReleaseShared()(说明见上);并返回TRUE
               3.返回false
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
       writeLock.lock()
            sync.acquire(1);
               1.执行tryAcquire(arg)
                   1.获取state锁状态属性
                   2.根据state计算出写锁的值(读锁和写锁 使用的是同一个字段state(利用分段高位低位存储不同的标识实现的)
                   3.如果state不为0
                       1.如果写锁为0(则读锁肯定不为0).或者当前拥有写锁的线程不是当前线程则返回false
                       2.如果写锁超过最大值,则抛出异常
                       3.使用setState更新最新的状态值(因为上面的判断,能走到这一步代表是当前线程重复获取读锁所以不需要使用cas
                       4.返回true
                   4.执行writerShouldBlock()判断是否需要挂起写线程,
                       1.如果是公平锁则通过hasQueuedPredecessors判断是否有前置等待的同步队列
                       2.如果是非公平锁则直接返回false,代表可以强占锁
                   5.如果上一步返回true,则尝试cas设置最新的锁状态,失败则返回false
                   6.获得锁成功,则执行setExclusiveOwnerThread(current);,将当前线程设置为独占锁的拥有线程
                   7.返回true
               2.如果获取锁失败则执行acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                   1.执行addWaiter(Node.EXCLUSIVE)方法,加入同步链
                   2.failed属性默认true
                   3.interrupted属性默认false;
                   4.进入自旋
                       1.如果当前节点的前置节点是head,则执行tryAcquire(arg)尝试获取所
                           1.如果成功,将node更新为head,这里不需要同步,因为只有一个线程可以获得锁
                           2.原head节点的next设置null
                           3.failed设置为false
                           4.返回interrupted属性.这里是false;
                       2.上一步尝试获取锁失败,则执行shouldParkAfterFailedAcquire判断是否需要挂起
                       3.如果需要挂起则执行parkAndCheckInterrupt()
                       4.再次被唤醒时如果parkAndCheckInterrupt()返回为true则将interrupted设置为true
                       5.继续下一轮自旋
                   5.在finally块中将failed等于true的节点执行 cancelAcquire(node);
       writeLock.unlock()
            sync.release(1);
               1.执行tryRelease(arg)
                   1.通过isHeldExclusively判断如果当前线程不是拥有独占所的线程则抛出异常
                       1.return getExclusiveOwnerThread() == Thread.currentThread();
                   2.计算nextc锁释放后的新值(因为是重入锁)
                   3.通过判断free = exclusiveCount(nextc) == 0来判断当前锁是否已经释放
                   4.如果已经彻底释放(free为true)则通过setExclusiveOwnerThread将独占锁拥有线程设置为null
                   5.更新state值为nextc
                   6.返回free
               2.如果完全释放锁,则通过unparkSuccessor唤醒后继节点线程并返回true.如果是重入锁直接返回了.不需要唤醒后继线程
               3.返回false,表示只是减少了的锁的数量,没有释放锁


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值