阅读 135

浅析ReentrantReadWriteLock

1. ReentrantReadWriteLock简介

ReentrantReadWriteLock适合处理读多写少的情况。在没有其他写线程持有该锁的情况下,同步状态是可以被多个读线程持有;如果当前同步状态被写锁持有,那么其他所有线程都将被阻塞。

2. ReentrantReadWriteLock实现

读写锁同样以来自定义同步器来实现同步功能,而读写状态就是其同步器的同步状态。ReentrantLock通过state标识锁的重入次数,那么ReentrantReadWriteLock也需要表示读锁的重入次数和写锁的重入次数,但是只有一个state变量。读写锁通过高16位表示读锁的重入次数低16位表示写锁的重入次数

S不等于0时,当写状态(S&0x0000FFFF)等于0时,则读状态(S>>>16)大于0,即读锁已被获取。

2.1 写锁的获取与释放

写锁是一个支持重入的排他锁。

  • 写锁的获取

写锁的获取是通过重写AQS中的tryAcquire方法实现的。当读锁已经被其他线程占有的时候,则写锁获取失败,否则支持重入,成功获得锁。

        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");
                //重入次数增加
                setState(c + acquires);
                return true;
            }
            //c=0,且读锁和写锁都没有被占用,判断写锁是否应该被阻塞
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }
复制代码

-写锁的释放

        protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            //同步状态减去写状态
            int nextc = getState() - releases;
            //判断当前线程的写状态是否为0,如果是0则释放写锁
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }
复制代码

2.2 读锁的获取与释放

  • 读锁的获取

如果其他线程获取到了写锁,则当前线程获取读锁失败,进入等待状态。如果当前线程获取了写锁或者写锁违背获取,则当前线程增加读状态,成功获取锁。

        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);
            //当前线程获取锁(读线程不应该被阻塞,读锁数量小于最大值,CAS成功)
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                //如果读锁的state为0说明美哟其他线程获取到锁
                if (r == 0) {
                    //把当前线程设为第一个获取到锁的线程
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    //当前线程是第一个获取到锁的线程,重入
                    firstReaderHoldCount++;
                } else {
                //读锁的状态不为0,且当前线程不是firstReader
                //最近一个成功获取到读锁的线程计数器
                    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);
        }
复制代码
  • 读锁的释放
        protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                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;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))

                    return nextc == 0;
            }
        }
复制代码
文章分类
后端
文章标签