Java并发Concurrent包的锁(四)——读写锁源码分析

本文详细分析了Java并发库Concurrent中的读写锁ReadWriteBarrierLock,探讨了ReadWriteLock接口的特性,它维护了读锁和写锁的独立访问。Read锁允许多个读线程并发,而Write锁是独占的。同时,文章对比了ReentrantReadWriteLock的NonfairSync和FairSync,解释了非公平模式与公平模式的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文对读写锁源码进行了比较详细的分析。
另外可以参考可重入锁 ReentrantLock 的分析:Java并发Concurrent包的锁(三)——ReentrantLock源码分析

ReadWriteLock接口

ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。

public interface ReadWriteLock {
    // 获得读锁
    Lock readLock();
    // 获得写锁
    Lock writeLock();
}

与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。写线程虽然一次只有一个访问数据,但读线程可以同时读取,而在实际中,读取往往都是大量的,写入是偶尔的,读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。
如果数据不常修改,而主要用于搜索,适合使用读写锁;而如果数据更新十分频繁,数据大部分时间都被每个线程所独占,这种情况并发也提高不了太多效率。而且如果读取的操作用时太多,那么读写锁本身的开销就是最大的执行成本。所以具体问题还是要具体分析。

ReentrantReadWriteLock的Sync

读写锁引用了 Sync 类,和 ReentrantLock 的 Sync 一样是通过 AbstractQueuedSynchronizer 进行扩展的,下面对其代码进行分析:

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 6317671515068378041L;

        /*
         * 锁状态state是int型,读写分为两部分,各用一个无符号short来表示
         * 低位的表示写锁即独享锁,高位的表示读锁共享数
         */
        // 这个16是两个字节,一个short的长度
        static final int SHARED_SHIFT   = 16;
        // 1左移16位,为00000000 00000001 00000000 00000000
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        // 1左移16位,减1,为65535,读写的获取锁的最大次数
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        // 右移16位,获取c高位的16位,表示读锁的共享总数(高位2字节全0)
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        // 和两个字节的全1字节与操作,高位2字节全0,获取写锁的总数
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

        // 每个线程的计数器,保存读锁共享数
        static final class HoldCounter {
            int count = 0;
            final long tid = getThreadId(Thread.currentThread());
        }

        // ThreadLocal的子类
        static final class ThreadLocalHoldCounter
            extends ThreadLocal<HoldCounter> {
            // 初始化方法,返回HoldCounter
            public HoldCounter initialValue() {
                return new HoldCounter();
            }
        }

        // 读线程的一个集合,但不包括最后一个读线程和第一个读线程
        private transient ThreadLocalHoldCounter readHolds;
        // 最后一个读线程的计数
        private transient HoldCounter cachedHoldCounter;
        // 第一个读线程
        private transient Thread firstReader = null;
        // 第一个读线程的计数
        private transient int firstReaderHoldCount;

        Sync() {
            readHolds = new ThreadLocalHoldCounter();
            setState(getState()); // ensures visibility of readHolds
        }
        // 读锁的阻塞
        abstract boolean readerShouldBlock();

        // 写锁的阻塞
        abstract boolean writerShouldBlock();

        // 释放写锁
        protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                // 当前线程不是owner线程,不能进行释放
                throw new IllegalMonitorStateException();
            // 计算释放后的状态值    
            int nextc = getState() - releases;
            // 如果当前写锁释放后,看是否还有写锁,没有写锁返回true
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                // 没有写锁了将锁对象的当前owner置为null
                setExclusiveOwnerThread(null);
            // 更新state的新值
            setState(nextc);
            return free;
        }

        // 尝试获取写锁
        protected final boolean tryAcquire(int acquires) {
            // 当前线程
            Thread current = Thread.currentThread();
            // 状态c,包含读锁和写锁的总数
            int c = getState();
            // w表示写锁的总数
            int w = exclusiveCount(c);
            if (c != 0) {
                // 如果写锁为0,说明当前有读锁(也可能是当前线程有读锁),不能获取写锁
                // 如果写锁不为0,但是当前线程不是owner线程不可重入,不能获取写锁
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                // 写锁数量超过了65535报错
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // 这里已经说明写锁是当前线程可重入的了,直接设置state值,返回true
                setState(c + acquires);
                return true;
            }
            // c==0时,如果写需要阻塞或者设置状态失败,返回false
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            // 设置锁的owner独占标识
            setExclusiveOwnerThread(current);
            return true;
        }

        // 尝试释放读锁
        protected final boolean tryReleaseShared(int unused) {
            // 当前线程current
            Thread current = Thread.currentThread();
            // 如果当前线程是第一个获取读锁的
            if (firstReader == current) {
                // 当前线程读锁总数为1,直接将firstReader置为null
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                // 如果不为1,那么减掉1即可
                else
                    firstReaderHoldCount--;
            } else {
                // 最后一个获取读锁的线程
                HoldCounter rh = cachedHoldCounter;
                // 如果当前线程也不是最后一个获取读锁的线程,从ThreadLocal中取
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                // 当前线程读锁的个数
                int count = rh.count;
                // 如果count==1,释放了就为0,直接在ThreadLocal中去掉当前线程
                if (count <= 1) {
                    readHolds.remove();
                    // count<=0,此时抛出不匹配释放锁的异常
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                // 正常情况下,将当前线程的读锁count值减1即可。
                --rh.count;
            }
            for (;;) {
                int c = getState();
                // 高位减1,新的读写锁值
                int nextc = c - SHARED_UNIT;
                // 尝试设置state值,预测之前为c,更新为nextc
                if (compareAndSetState(c, nextc))
                    // 释放读锁不会影响其他读锁,但是如果这时读写锁都空闲,等待中的写线程将开始执行
                    // 这里只有读写锁都释放了,才返回true
                    return nextc == 0;
            }
        }

        // 释放读锁的不匹配异常
        private IllegalMonitorStateException unmatchedUnlockException() {
            return new IllegalMonitorStateException(
                "attempt to unlock read lock, not locked by current thread");
        }

        // 尝试获取读锁
        protected final int tryAcquireShared(int unused) {
            // 当前线程
            Thread current = Thread.currentThread();
            // 锁的state值
            int c = getState();
            // 如果有独占标识同时当前线程并不是独占线程时,直接返回-1
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            // 读锁的总数
            int r = sharedCount(c);
            // 如果读不需要阻塞,同时读锁小于65535,并且能够CAS设置c的值读个数加1
            // 这里的阻塞策略readerShouldBlock进行了判断,有两种情况:
            // 1.公平模式:如果AQS队列前面有等待的结点,当前线程应该阻塞
            // 2.非公平模式:如果AQS前面有线程在等待写锁,当前线程应该阻塞(这样做的原因是为了防止写饥饿)。
            // 出现这个阻塞情况,就不走下边的if了,直接到最后走fullTryAcquireShared方法处理
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                // 读锁个数为0,将当前线程作为第一个读线程单独保存,不存在ThreadLocal中
                if (r == 0) {
                    firstReader = current;
                    // 当前线程即第一个读线程的读锁总数置为1
                    firstReaderHoldCount = 1;
                // 如果r!=0,说明有读线程了,判断当前线程是不是第一个读线程
                } else if (firstReader == current) {
                    // 如果当前线程就是第一个读线程,数量count加1即可
                    firstReaderHoldCount++;
                } else {
                    // 当前线程不是第一个读线程,判断是不是最后一个读线程
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        // 当前线程不是最后一个读线程,从ThreadLocal中取线程
                        // 同时将最后一个读线程更新为当前线程
                        cachedHoldCounter = rh = readHolds.get();
                    // 这里如果当前线程就是最后一个读线程的话,如果count为0
                    else if (rh.count == 0)
                        // 需要重新set当前线程(之前释放时,readHolds.remove()进行过这个操作)
                        readHolds.set(rh);
                    // 将当前线程的count加1
                    rh.count++;
                }
                // 返回值1说明获取读锁成功了
                return 1;
            }
            // 读线程阻塞时,执行这个方法
            return fullTryAcquireShared(current);
        }

        // 该方法处理读线程阻塞的情况
        final int fullTryAcquireShared(Thread current) {
            HoldCounter rh = null;
            for (;;) {
                int c = getState();
                // 独占标记存在且当前owner不是当前线程,不能重入
                if (exclusiveCount(c) != 0) {
                    if (getExclusiveOwnerThread() != current)
                        return -1;
                // 没有写锁存在时,如果读阻塞
                // 这里的阻塞策略,如果:1.公平模式:如果当前AQS队列前面有等待的结点,返回false;2.非公平模式:如果
// AQS前面有线程在等待写锁,返回false(这样做的原因是为了防止写饥饿)。
                } else if (readerShouldBlock()) {
                    // 如果当前线程是第一个读线程
                    if (firstReader == current) {

                    } else {
                        if (rh == null) {
                            // 优先赋值成上一次获取读锁成功的cache,即最后一个读锁
                            rh = cachedHoldCounter;
                            if (rh == null || rh.tid != getThreadId(current)) {
                                // 如果和当前线程不匹配,从ThreadLocal中取
                                rh = readHolds.get();
                                if (rh.count == 0)
                                    readHolds.remove();
                            }
                        }
                        // 不是读锁重入的情况,返回-1
                        if (rh.count == 0)
                            return -1;
                    }
                }
                // 如果读锁总数为65535,报错
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // 如果CAS将c的读锁总数增加1成功
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    // 如果读锁数量为0,当前线程作为第一个线程存起来
                    if (sharedCount(c) == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    // 当前线程为第一个获取读锁的线程,数量加1
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    // 其他情况,和之前的方法同样原理
                    } else {
                        if (rh == null)
                            rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                        cachedHoldCounter = rh; // cache for release
                    }
                    return 1;
                }
            }
        }

        // 和tryAcquire基本一致,去掉了考虑阻塞部分,相当于非公平模式的获取写锁
        final boolean tryWriteLock() {
            Thread current = Thread.currentThread();
            int c = getState();
            if (c != 0) {
                int w = exclusiveCount(c);
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
            }
            if (!compareAndSetState(c, c + 1))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }

        // 和tryAcquireShared函数基本一致,去掉了考虑阻塞部分,相当于非公平模式的获取读锁
        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;
                }
            }
        }

        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        final Thread getOwner() {
            return ((exclusiveCount(getState()) == 0) ?
                    null :
                    getExclusiveOwnerThread());
        }

        final int getReadLockCount() {
            return sharedCount(getState());
        }

        final boolean isWriteLocked() {
            return exclusiveCount(getState()) != 0;
        }

        final int getWriteHoldCount() {
            return isHeldExclusively() ? exclusiveCount(getState()) : 0;
        }

        final int getReadHoldCount() {
            if (getReadLockCount() == 0)
                return 0;

            Thread current = Thread.currentThread();
            if (firstReader == current)
                return firstReaderHoldCount;

            HoldCounter rh = cachedHoldCounter;
            if (rh != null && rh.tid == getThreadId(current))
                return rh.count;

            int count = readHolds.get().count;
            if (count == 0) readHolds.remove();
            return count;
        }

        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            readHolds = new ThreadLocalHoldCounter();
            setState(0); // reset to unlocked state
        }

        final int getCount() { return getState(); }
    }

NonfairSync和FairSync

非公平模式

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        final boolean writerShouldBlock() {
            // 不公平模式下,写锁应该始终可以插队,写线程不阻塞
            return false; 
        }
        final boolean readerShouldBlock() {
            // 不公平模式下,如果队列前端有写线程等待,那么读应该阻塞
            return apparentlyFirstQueuedIsExclusive();
        }
    }
    // 当第一个节点的thread是等待写锁时,返回true
    final boolean apparentlyFirstQueuedIsExclusive() {
        Node h, s;
        return (h = head) != null &&
            (s = h.next)  != null &&
            !s.isShared()         &&
            s.thread != null;
    }

公平模式

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            // 公平模式,有等待的其它线程,写应该阻塞
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            // 公平模式,有等待的其它线程,读应该阻塞
            return hasQueuedPredecessors();
        }
    }
    // 公平模式下,判断队列前边的节点是否有等待的其它线程(非当前线程不能重入),返回true
    public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值