从ReentrantReadWriteLock开始入手AQS源码

ReentrantReadWriteLock是一把读写锁,其基本实现依赖于两把锁,读锁和写锁,读锁之间能够进入,写锁之间互斥,这样对于锁的操作更加细粒度。首先要学习任何东西,都得了解他是用来干嘛的,其次知道他的流程,接下来就是真正的深入研究。下面放出一个我觉得不错的流程讲解:轻松掌握java读写锁(ReentrantReadWriteLock)的实现原理

1.构造器

public ReentrantReadWriteLock() {
    //公平与否
    //默认是不公平锁    
    this(false);
}

public ReentrantReadWriteLock(boolean fair) {
        //总锁(真正的锁)
        sync = fair ? new FairSync() : new NonfairSync();
        //读锁,实现了lock接口
        readerLock = new ReadLock(this);
        //写锁,实现了lock接口
        writerLock = new WriteLock(this);
}
//从这个地方就可以看出,其实只有一把锁,那就是sync,并且sync应该是支持两把锁的
//一把共享锁,一把是独占锁

2.有个东西不讲不行,那就是读写锁如何保存状态的?reentrantLock是利用一个state来说明锁的占用情况。那么sync也有一个变量state,他是利用了32位的int,高16位代表读锁,低16位代表写锁。

static final int SHARED_SHIFT   = 16;
//读锁+1 的单位
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
//重入的最大值65535
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//读锁的数量
static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
//写锁的数量
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

3.那么接下来看一下锁的操作。尝试了很多次从读锁开始下手,发现有点难度,就换了一个角度,那么我们就从写锁的操作开始入手,先走一个不竞争锁的过程,然后再分析有锁竞争的流程。writerLock.lock()

public void lock() {
      sync.acquire(1);
}
//还是进入了aqs的方法中
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
         
            Thread current = Thread.currentThread();
            //查看有没有读锁或者写锁
            int c = getState();
            //得到当前的写锁数量
            int w = exclusiveCount(c);
            //有其他锁的情况,我们稍后分析
            if (c != 0) {
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                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))
                return false;
            //占有独占锁
            setExclusiveOwnerThread(current);
            return true;
}

4.既然锁住了,那么就需要解锁了。writerLock.unlock()。

public void unlock() {
      
    sync.release(1);
}
//其实一定会成功
public final boolean release(int arg) {
        //尝试解锁
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
}
protected final boolean tryRelease(int releases) {
            //判断 当前线程是不是占有锁的线程
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                //将独占锁取消
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
}

5.假设现在考虑写锁需要排队了,之前存在一个写锁。

//第二把锁走到这一步
protected final boolean tryAcquire(int acquires) {
           
            Thread current = Thread.currentThread();
            //c!=0
            int c = getState();
            //w=1
            int w = exclusiveCount(c);
            if (c != 0) {
                //就会走这一步
                //(1)判断有没有写锁
                //(2)判断是否是当前线程重入
                //这个地方还有点复杂,得结合读锁来分析,先放一下。
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                //判断重入最大数 是否超过65535
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                //这个地方也代表了 state是重入数量
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
}
//接下来 又要到aqs里面了
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        //判断队列是否初始化,如果没有初始化,就enq(node)进行初始化
        //如果初始化了,那么就通过cas放入链尾
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
}
//剩下的就是和reentrantLock一样了

6.然后看一下读锁,读锁有点复杂了,涉及的东西也特别的多,因此还是得一步步地走下去。readLock.lock()

public void lock() {
      //获取共享锁
      sync.acquireShared(1);
}
public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
}

protected final int tryAcquireShared(int unused) {
            
            Thread current = Thread.currentThread();
            int c = getState();
            //查看是否有写锁的存在,并且该写锁是否属于该线程
            //如果写锁线程不是该线程,那么就获取失败
            //这里涉及了锁降级
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            //查看是否有读锁
            int r = sharedCount(c);
            //读锁是否应该被阻塞
            //对于公平锁来说,就是队列当中存在排队,那么就得排队
            //对于非公平锁来说,需要判断:为了避免永久轮不到写锁
            //那么当写锁是在队列的第二个位置的时候,就会让读锁排队
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                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 1;
            }
            return fullTryAcquireShared(current);
}
//非公平锁readerShouldBlock最终进了这个方法
final boolean apparentlyFirstQueuedIsExclusive() {
        Node h, s;
        //其实就是判断队列老二是不是共享的
        return (h = head) != null &&
            (s = h.next)  != null &&
            !s.isShared()         &&
            s.thread != null;
}

7.那么读锁肯定也要涉及释放的。readLock.unlock()。

public void unlock() {
      sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
        //尝试释放
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
}
protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            //读锁还原 数量-1
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    // 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;
            }
}

-------------------------------------------------------------------------以后再补充了,太过于复杂了----------------------------------------------------

猜你喜欢

转载自blog.csdn.net/qq_40384690/article/details/81090222