0
点赞
收藏
分享

微信扫一扫

ReentrantReadWriteLock之读锁的释放源码解析

ReentrantReadWriteLock是Java中读写锁的一种实现,他可以让多个线程同时读取共享资源——读共享,写入的时候只允许一个线程独占——写独占.了解过ReentrantLock的应该知道这个是一个可重入锁,允许一个线程可以多次获取相同类型的锁.在读操作多于写操作的场景下,读写锁能够提高读操作的并发性,同时保证写操作的独占性.

读锁的使用

ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReadLock readLock = reentrantReadWriteLock.readLock();
try{
  readLock.lock();
}catch(Exception e){
  log.error("加锁失败!");
}finally{
  readLock.unlock();
}

上篇文章已经对lock()加锁方法进行的分析,本篇文章主要对解锁进行分析

public void unlock() {
      sync.releaseShared(1);
  }

Sync是AbstractQueuedSynchronizer的子类,实现加锁和解锁主要是使用AQS来实现的.

public final boolean releaseShared(int arg) {
    // 尝试释放锁,并且根据返回值来决定是否需要唤醒后续结点
    if (tryReleaseShared(arg)) {
        // 唤醒后续结点
        doReleaseShared();
        return true;
    }
    return false;
}

下面分别来看下上面两个关键方法

tryReleaseShared(arg)

protected final boolean tryReleaseShared(int unused) {
    // 获取当前线程
    Thread current = Thread.currentThread();
    // 判断当前线程是不是当前获取读锁的第一个线程
    if (firstReader == current) {
        // 如果是第一个获取锁的线程,则判断有没有重入加锁过
        if (firstReaderHoldCount == 1)
            // firstReaderHoldCount == 1:说明只加过1次读锁
            // 因此直接把 firstReader的记录清掉
            firstReader = null;
        else
            // firstReaderHoldCount != 1:说明重入过读锁
            // 将加锁次数减1
            firstReaderHoldCount--;
    } else {
        // 如果不是第一个加锁的线程,则获取cachedHoldCounter缓存中的记录
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current))
            // 如果cachedHoldCounter缓存是空的,或者缓存的不是当前线程
            // 则需要从readHolds中来获取
            rh = readHolds.get();
        // count:
        //      == 1:说明只加过1次读锁
        //		== 0:说明没有加过锁,此时释放会导致报错
        int count = rh.count;
        if (count <= 1) {
            // 因为只加过1次锁或压根没有加过锁,直接从readHolds中移除
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        // 加锁次数减1
        --rh.count;
    }
    for (;;) {
        // 获取state的值
        int c = getState();
        // 相当于state的高16位减1
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            // 如果当前所有的读锁都被释放了,则返回
            return nextc == 0;
    }
}

doReleaseShared()

当上个方法最后return nextc == 0;返回true的时候,就会触发doReleaseShared()执行,意思是当所有的读锁和写锁都释放完毕.

private void doReleaseShared() {
    for (;;) {
        // 每次获取最新的head
        Node h = head;
        // head不是空,并且也不等于尾结点
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;  
                // ws == Node.SIGNAL:后续节点等待激活状态
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                // 如果waitstatus为0(初始化状态),则将head的状态设置为PROPAGATE(代表共享等待模式,下一次获取共享锁无条件传播)
                continue;                
        }
        if (h == head)  
            // 当前头结点没有变化
            break;
    }
}

其实上一篇文章已经介绍了关于doReleaseShared(),这里就不再多说.

举报

相关推荐

0 条评论