ReentrantReadWriteLock 源码

1 tryAcquire 写锁获取锁,独占模式尝试获取资源:如果其他线程获取了写锁,或者当前线程或其他线程线程获取了读锁,则失败返回。如果当前线程获取了写锁,则重入锁,资源数+1,成功返回。如果资源没有被获取,非公平锁直接cas操作对资源数+1,把当前线程保存到锁上成功返回,公平锁如果aqs线程等待队列有等待的线程,则失败返回。   如果线程持有了读锁,若当前线程申请写锁,会产生死锁:当前线程在独占模式尝试获取资源时候,失败返回,去AQS线程等待队列等待获取资源,因为它永远无法获取资源成功,则在线程等待队列中排在它后面的节点线程永远没有机会尝试获取资源,导致永远在线程等待队列等待,永远无法获取写锁。

2 tryRelease 写锁尝释放锁,独占模式尝试释放资源:如果当前线程没有占有锁,抛出异常。对资源数-1保存,如果新资源数为0则把占有锁的线程置为null,返回true,不是0,则返回fals。  重入锁,会做多次释放,最终资源数变为0.

firstReader:第一个获取读锁的线程。
firstReaderHoldCount:第一个获取读锁的线程持有的资源数。
cachedHoldCounter:HoldCounter缓存。降低了直接去readHolds里维护HoldCounter的消耗。
readHolds:是一个ThreadLocal,保存了线程本地的HoldCounter。

readerShouldBlock 非公平锁,线程等待队列中第二个节点等待独占资源,返回true,其他返回false。公平锁,线程等待队列中有其他节点线程等待资源,返回true,其他返回false。

3 tryAcquireShared 读锁获取锁,共享模式尝试获取资源:如果其他线程获取了写锁,失败返回。如果不满足 读锁需要等待(非公平锁:如果线程等待队列存在第二个节点,且第二个节点等待获取独占资源则需要等待,否则不需要等待。公平锁:如果线程等待队列中有节点等待获取资源,则需要等待,否则不需要等待。)且cas修改资源数,资源数+1成功【如果读锁资源还没有被占有,则记录当前线程为第一个获取读锁线程,并对第一个获取读锁线程占有的资源数初始化为1,否则如果第一个获取读锁的线程是当前线程,则对第一个占有读锁的线程占有的资源+1,否则 在HoldCounter缓存获取HoldCounter,如果HoldCounter里保存的线程和当前线程不是同一个线程,则在ThreadLocal里取出HoldCounter保存到缓存里,如果是同一个线程,且资源数=0,则把HoldCounter保存到ThreadLocal里,对HoldCounter持有资源数+1,最后成功返回。】如果满足 读锁需要等待,或者cas修改资源数+1失败,则执行full尝试获取逻辑。

4 full尝试获取逻辑:自旋::如果其他线程获取了写锁,失败返回。【如果写锁没有被获取,且满足读锁需要等待(公平锁:如果线程等待队列有节点线程等待资源,则需要等待,否则不需要等待。非公平锁:如果线程等待队列有第二个节点,且第二个节点等待独占资源,则需要等待否则不需要等待。)则 如果当前线程的HoldCount没有初始化,则初始化,初始化过程:先在缓存里取出HoldCount,如果线程不是当前线程则在ThreadLocal里取出HoldCount,如果资源数是0则在ThreadLocal移除。初始化后,如果当前线程持有的资源数是0,则说明不是重入锁,失败返回】。【如果cas操作修改读锁资源数+1成功,则:如果读锁没有被获取过,则把当前线程标记为首个获取读锁的线程,且初始化首个获取读锁的线程的资源数为1,否则 如果当前线程和首个获取读锁的线程为同一个线程,则为首个线程的重入锁,对首个获取读锁的线程持有的资源数+1,否则 在缓存中取出HoldCount,如果它的线程和当前线程不相同,则在ThreadLocal里取出HoldCount,把HoldCount保存到缓存里,如果相同且持有的资源数是0,则把它保存到ThreadLocal里,HoldCount资源数+1,成功返回。】如果cas操作失败,自旋。
    说明:cas操作之前的逻辑是让重入锁,自旋,而不是失败返回,进入aqs队列排队,目的是为了防止死锁。因为 如果这个线程持有了读锁,再次申请读锁,如果读锁需要等待,如果这个时候直接失败返回,去线程等待队列排队,若在队列中已经有个线程等待写锁,则形成死锁,因为等待写锁的线程需要当前线程释放读锁,当前线程重入读锁,需要等待前面的节点线程获取写锁成功且释放后,才能获取读锁,产生死锁。因此为了避免死锁,重入锁宁可失败自旋,也不失败进入队列。

5 tryAcquireShared 获取读锁:使用共享模式尝试获取资源: 如果其他线程获取了写锁,则失败返回。如果 写锁需要等待(非公平锁线程等待队列存在第二个节点,且等待获取独占资源返回true,否则返回false;公平锁 如果线程等待队列有节点线程等待获取资源,返回true,否则返回false)返回false且cas修改读锁资源数+1成功【如果读锁还没有被获取,则把当前线程标记为首个获取读锁的线程,并且初始化首个持有读锁的线程占有的资源数为1,如果当前线程是首个持有读锁的线程,则把首个持有读锁的线程占有的读锁资源数+1,否则 在缓存中拿到HoldCounter,如果和当前线程不同,则在ThreadLocal中获取HoldCounter,把它放置到缓存里,如果和当前线程相同,则判断如果持有的读锁资源数是0,则把它保存到ThreadLocal里,把holdCounter的资源数+1成功返回。】读锁需要等待返回true或者cas修改资源数失败,执行升级版获取读锁的逻辑,为了防止死锁,重入锁不会失败返回,而是不断的自旋,直到获取资源成功。因为如果不加这个限制,可能会出现死锁,比如一个线程获取了读锁,如果其他线程申请写锁,尝试失败则进入aqs线程等待队列排队,如果持有读锁的线程重入,由于读锁需要等待失败,进入aqs线程等待队列排队,而写锁节点线程等待读锁释放,而读锁节点线程等待写锁节点线程,出现死锁。升级版获取读锁的逻辑,避免了这种情况,逻辑:自旋 如果其他线程获取了写锁,则失败返回。【如果写锁没有被获取且读锁需要等待且当前线程不是首个获取读锁的线程:如果HoldCounter没有被初始化,则初始化,先在缓存获取,如果缓存里获取的HoldCounter线程和当前线程不是同一个线程,则在ThreadLocal获取完成初始化。如果HoldCounter的资源数为0,则在ThreadLcoal中移除。如果HoldCounter资源数是0(说明不是重入锁),失败返回。】cas修改读锁资源数+1,如果成功,如果读锁还没有被获取,则把当前线程设置为首个获取读锁的线程,初始化首个获取读锁的资源数为1,如果当前线程是首个获取读锁的线程,则对首个获取读锁的线程资源数+1,否则在缓存获取HoldCounter,如果和当前线程不是同个线程,则在ThreadLocal获取,保存到缓存里,如果是同个线程且持有资源数是0,则保存到ThreadLocal里,对资源数+1,成功返回。如果没有返回 继续自旋。

6 tryReleaseShared 释放读锁 共享模式尝试释放资源:如果当前线程是首个获取读锁的线程,如果首个持有读锁的线程持有的资源数==1,则设置首个获取读锁的线程为null.否则 对首个持有读锁的线程持有的资源数-1。否则 在缓存中获取HoldCounter,如果和当前线程不是同一个线程,在ThreadLocal中获取HoldCounter,如果资源数<=1,则 在ThreadLocal移除,如果资源数<=0,则说明没有获取读锁,抛出异常,对持有的资源数-1.自旋 cas修改读锁资源数-1,成功后如果 剩余总资源数为0,返回true(读写锁都已经释放),否则返回false。        
  

猜你喜欢

转载自blog.csdn.net/liangwenmail/article/details/81297869