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,表示只是减少了的锁的数量,没有释放锁