ReentrantReadWriteLock 源码分析

一 总体认识读写锁

  读写锁,简单点说

  1 如果当前有读锁,获取读锁不阻塞

  2 如果当前是读锁,获取写锁阻塞

  3 如果当前是写锁,同一个线程获取读锁不阻塞

  4 当前是读锁,无论是否同一线程获取读锁和写锁都阻塞

  5 写锁支持Condition 读锁不支持

  引用下别人总结的 

1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。

(2)重进入:读锁和写锁都支持线程重进入。

(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁。

二 关键属性

  在分析源码的方法前,先说明下关键的字段有助于理解方法

public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {

    /** 读锁 */
    private final ReentrantReadWriteLock.ReadLock readerLock;

    /** 写锁 */
    private final ReentrantReadWriteLock.WriteLock writerLock;

    final Sync sync;
    
    /** 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock */
    public ReentrantReadWriteLock() {
        this(false);
    }

    /** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    /** 返回用于写入操作的锁 */
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    
    /** 返回用于读取操作的锁 */
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }


    abstract static class Sync extends AbstractQueuedSynchronizer {}

    static final class NonfairSync extends Sync {}

    static final class FairSync extends Sync {}

    public static class ReadLock implements Lock, java.io.Serializable {}

    public static class WriteLock implements Lock, java.io.Serializable {}
}

  和重入锁一样,都是内部有一个AQS的实现类,然后事情都交给它去干,这种组合方式。所以呢,关键点就在于  abstract static class Sync extends AbstractQueuedSynchronizer 

三 Sync源码分析

  Sync类内部存在两个内部类,分别为HoldCounter和ThreadLocalHoldCounter。HoldCounter的意义是记录每个线程读锁的重入次数

static final class HoldCounter {
    // 计数
    int count = 0;
    // Use id, not reference, to avoid garbage retention
    // 获取当前线程的TID属性的值
    final long tid = getThreadId(Thread.currentThread());
}

  继承了ThreadLocal的 ThreadLocalHoldCounter用来保存每个线程的读锁重入次数

static final class ThreadLocalHoldCounter
    extends ThreadLocal<HoldCounter> {
    // 重写初始化方法,在没有进行set的情况下,获取的都是该HoldCounter值
    public HoldCounter initialValue() {
        return new HoldCounter();
    }
}

  

     static final int SHARED_SHIFT   = 16;//高16位是读锁
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);//相当于1后面16个0,每次读锁重入一次就加这个数
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;//高16位都是0低16位都是1

        /** Returns the number of shared holds represented in count  */
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }//读锁的重入次数
        /** Returns the number of exclusive holds represented in count  */
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }//计算写锁的重入次数

  下面按照排他锁,到重入锁的顺序依次分析 ,先来看锁的获取

排他锁

    protected final boolean tryAcquire(int acquires) {
            
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);//排他锁重入次数
            if (c != 0) {//不为0说明可能有读锁也可能有写锁,还得进一步判断
                // (Note: if c != 0 and w == 0 then shared count != 0)
                if (w == 0 || current != getExclusiveOwnerThread())//如果此时写锁为0,说明是读锁,那么获取写锁肯定失败。或者持有锁的线程不是当前线程也失败
                    return false;//当前线程包成Node进入同步队列中阻塞
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);//增加重入次数
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))//这个分支说明c==0,那就通过cas的方法抢锁 writeShouldBlock是公平锁才会实现的,主要检查同步队列中如果有线程等待,当前线程就进队列去排队
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }

  从上面的分析,发现了吧和重入锁并没有多大的差别,只不多增加了检查读锁重入数的逻辑其他的都一样的

  再来是锁的释放

      protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;//释放一定是在持有锁情况下,所以不需要用CAS
            boolean free = exclusiveCount(nextc) == 0;//排他锁是否等于0
            if (free)//如果等于0,释放锁
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;//返回true,就会执行AQS中的释放逻辑,唤醒
        }

  释放逻辑也不难,就是判断排他锁的重入数是否等于0,如果等于0,返回true,执行释放逻辑

  

  final boolean tryWriteLock() {
            Thread current = Thread.currentThread();
            int c = getState();
            if (c != 0) {
                int w = exclusiveCount(c);
                if (w == 0 || current != getExclusiveOwnerThread())//写锁数是0那就说明有读锁
                    return false;
                if (w == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
            }
            if (!compareAndSetState(c, c + 1))//CAS失败说明有别的线程改动了state
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }

 

  接下来是共享锁部分

共享锁

  获取共享锁逻辑

    protected final int tryAcquireShared(int unused) {
            
            Thread current = Thread.currentThread();
            int c = getState();
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)//如果此时排他锁不为0,并且持有锁的线程不是当前线程,返回-1。这也说明了如果持有锁的线程是当前线程就能获得读锁了
                return -1;
            int r = sharedCount(c);//当前的读锁总数量
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {//CAS方式读锁总量加1
                if (r == 0) {//如果此时没有任何读锁
                    firstReader = current;//给firstReader赋值
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {//先从ThreadLocal里取出来重入数,再加1
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);//这个是由于上面的if不满足而进入的,其实逻辑和上面大差不差的。不满足可能是由于CAS的时候失败了,此方法会通过自旋的方式执行上面的逻辑
        }

  释放共享锁逻辑

protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {//还是处理firstReader问题
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))//如果不是缓存的计数器,那么就从Threadlocal里拿
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {//如果小于1了,释放后就是0了,这时就要清理ThreadLocal
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {//上面的都是处理每个线程的读锁计数问题的,都不是什么关键流程
                int c = getState();
                int nextc = c - SHARED_UNIT;//共享锁计数-1
                if (compareAndSetState(c, nextc))//通过CAS设置新值
                    // 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;//如果-1之后等于0,那就是说明应该释放锁了
            }
        }

  

    final boolean tryReadLock() {
            Thread current = Thread.currentThread();
            for (;;) {
                int c = getState();
                if (exclusiveCount(c) != 0 &&
                    getExclusiveOwnerThread() != current)//其实这部分代码和共享锁的获取逻辑差不多的,只不过可以独立调用不用阻塞
                    return false;
                int r = sharedCount(c);
                if (r == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    if (r == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        HoldCounter rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            cachedHoldCounter = rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                    }
                    return true;
                }
            }
        }

四 总结

  源码分析下来,觉得读写锁如果之前的基础打好了,理解起来并不难!

 

  

posted on 2020-11-27 20:03  MaXianZhe  阅读(87)  评论(0编辑  收藏  举报

导航