JUC并发编程 - ReentrantReadWriteLock源码分析

7 篇文章 0 订阅
7 篇文章 0 订阅

前言

ReentrantReadWriteLock锁是AQS的另一种实现,它做到了可重入、可中断,分为公平和非公平两类实现,并且实现了读锁和写锁两类同时控制。在使用时,读写锁持有的同一个Lock实例,通过控制锁的行为,及CLH节点状态,来操作读锁和写锁,对于写少读多的场景,能提高效率。

由于我对AQS及ReentrantLock的源码已经做过分析,因此本篇文章对经常出现的代码流程介绍的会较为粗略,初次接触ReentrantReadWriteLock源码的同学,建议先从前面的文章看起,这样反而更能清晰的理解。

面试必考AQS-AQS源码全局分析​

面试必考AQS-排它锁的申请与释放​

面试必考AQS-共享锁的申请与释放,传播状态

类的定义

读写锁ReentrantReadWriteLock,实现接口ReadWriteLock,其内部定义了两个方法,分别获取读锁和写锁。

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

看一下ReentrantReadWriteLock内部相关实现:

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

    private final ReentrantReadWriteLock.ReadLock readerLock;    
    private final ReentrantReadWriteLock.WriteLock writerLock;
    final Sync sync;

    public ReentrantReadWriteLock() {
        this(false);
    }

    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; }
 
    // ...
}        

与ReadWriteLock接口相关的代码,包含两个锁的实例、一个同步器Sync、两个构造函数,其中无参构造函数默认实现了非公平同步器;初始化两个读写锁实例时,将this传入;接下来看一下读写锁类的实现。

实现Lock接口,内部持有一个Sync实例,由构造函数注入。

public static class ReadLock implements Lock, java.io.Serializable {
    private final Sync sync;
    protected ReadLock(ReentrantReadWriteLock lock) {
        sync = lock.sync;
    }
    // ...
}

在翻看ReadLock的实例方法,都是实现的Lock接口,与ReentrantLock相似。而WriteLock的定义与ReadLock差不多,具体分析时再看。

直接进入重点AQS类的实现类Sync,在ReentrantReadWriteLock中,与ReentrantLock类似,分为公平同步器和非公平同步器,并且默认实现的是非公平同步器。

abstract static class Sync extends AbstractQueuedSynchronizer {...}

在Sync中定义了一组常量及方法,用于记录读锁和写锁数量,它们是:

static final int SHARED_SHIFT   = 16; // 共享锁偏移量
static final int SHARED_UNIT    = (1 << SHARED_SHIFT); // 1左移16位,共享锁的位置
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1; // 允许申请锁的最大数量
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;// 得到低16位补码;这样的int值EXCLUSIVE_MASK的高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; }

对于AQS的state值,存储的是一个32位int,在ReentrantReadWriteLock中,将高16位记录读锁数量,低16位记录写锁数量。

通过位移等运算,快速的得到需要的值,如sharedCount(int c),传入的c既是state值,通过无符号右移16位,快速得到读锁/共享锁的数量;

而exclusiveCount(int c)方法,传入state的值c,通过与EXCLUSIVE_MASK做&操作,能快速得到低16位的值是多少,也就是写锁/排它锁的数量。

c = 0110 0011 0000 1001 0000 0000 0000 0011
c & 0000 0000 0000 0000 1111 1111 1111 1111
    0000 0000 0000 0000 0000 0000 0000 0011 // 因此有4个排它锁


在这之后,还定义了四个成员变量:

private transient ThreadLocalHoldCounter readHolds;
private transient HoldCounter cachedHoldCounter;
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;

他们的作用在后面使用到的时候再介绍,这里看类的实现:

static final class HoldCounter {
    int count = 0;
    final long tid = getThreadId(Thread.currentThread());
}
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
    public HoldCounter initialValue() {
        return new HoldCounter();
    }
}
static final long getThreadId(Thread thread) {
    return UNSAFE.getLongVolatile(thread, TID_OFFSET);
}

实现了ThreadLocal<?> ,记录了count和tid两个值,看样子是保持某些信息的。

在Sync中还有两个抽象方法

abstract boolean readerShouldBlock();
abstract boolean writerShouldBlock();

既然默认实现的是非公平同步器,那么我们来看一下NofairSync的实现,内容很简单:

static final class NonfairSync extends Sync {
    final boolean writerShouldBlock() {
        return false; // 永远返回false
    }
    final boolean readerShouldBlock() {
        return apparentlyFirstQueuedIsExclusive(); // 调用一个AQS方法
    }
}
final boolean apparentlyFirstQueuedIsExclusive() {
    Node h, s;
    return (h = head) != null &&
        (s = h.next)  != null &&
        !s.isShared()         &&
        s.thread != null;
}

同步队列的首个节点不为空、队列不止一个节点、非head节点不是共享(存在排它锁等待)、非head节点的线程不为空。以上条件都满足,则认为当前读锁需要阻塞。

零散的源码介绍的差不多,到这应该已经一头雾水了,没关系,下面开始由读写锁的行为分析源码设计,并将上述内容串起来。

非公平同步器实现的写锁

定义了一个Sync类型对象sync,由构造函数传入,也就是这里定义了当前写锁是由“公平同步器实现”还是由“非公平同步器实现”实现的。

private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
    sync = lock.sync;
}

排它锁加锁流程分析

在WriteLock中,加锁方法直接使用的是sync.acquire(1); 追溯起来就是AQS的acquire()申请流程。

public void lock() {
    sync.acquire(1);
}
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

那么tryAcquire()的实现,则是Sync当中了:

protected final boolean tryAcquire(int acquires) {
    /*
     * Walkthrough:
     * 1. If read count nonzero or write count nonzero
     *    and owner is a different thread, fail.
     * 2. If count would saturate, fail. (This can only
     *    happen if count is already nonzero.)
     * 3. Otherwise, this thread is eligible for lock if
     *    it is either a reentrant acquire or
     *    queue policy allows it. If so, update state
     *    and set owner.
     */
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);
    if (c != 0) {
        // (如果 c != 0 并且写锁数量为0,那么读锁数量肯定不为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;
}

方法内有大段的注释,翻译一下:

1、如果读锁数量不为0 或者 写锁数量不为零,并且 线程拥有者不是当前线程,获取失败
2、如果锁的数量已经达到最大值,获取失败
3、以上不满足,说明 可以申请锁,如果设置state成功,说明获取成功

这3条,囊括了tryAcquire()方法c!=0的大部分逻辑。

在代码中,if (w == 0 || current != getExclusiveOwnerThread()) 的潜在含义有2点:

1、当读写锁中存在读锁时,是不能直接申请到写锁的;
2、读锁是可以重入的

当c==0的情况下,也就是读写锁都为0,此时做了如下处理:

if (writerShouldBlock() ||
    !compareAndSetState(c, c + acquires))
    return false;
setExclusiveOwnerThread(current);
return true;

由于当前分析的是非公平同步器的实现,因此writerShouldBlock()==false。那么这段的逻辑就是 尝试直接修改state的值,如果设置成功,那么申请锁成功,设置排他线程拥有者、返回true。

在调用完tryAcquire(),后续流程就是AQS标准流程,这个在前文中已经详细描述过,这里不再赘述。

排它锁的tryLock

在WriteLock中实现的tryLock()方法,它的主逻辑是由Sync实现的。

public boolean tryLock( ) {
    return sync.tryWriteLock();
}
final boolean tryWriteLock() {
    Thread current = Thread.currentThread(); // 获取当前线程
    int c = getState();// 获取资源
    if (c != 0) { // 有锁
        int w = exclusiveCount(c); // 计算排它锁数量
        if (w == 0 || current != getExclusiveOwnerThread())  // 与tryAcquire逻辑一致
            return false;
        if (w == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
    }
    if (!compareAndSetState(c, c + 1))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}

在tryWriteLock()方法中,相当于是单独执行了一次tryAcquire(),并且只是对state做+1操作。只是不需要考虑排它锁的阻塞情况,这也进一步说明“非公平”的特点。

排它锁带超时的tryLock

带有超时时间的tryLock,实现流程较为复杂,先看一下基本的调用关系:

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

执行同步器的tryAcquireNanos()方法,具体的会执行tryAcquire()尝试直接申请一次锁,如果不成功会执行AQS的doAcquireNanos()。

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

这个方法在分析AQS时做过详细分析,这里再写一下粗略的过程:

1、将当前线程封装为node节点,并加入同步队列,计算超时时间点
2、如果node的前置节点是head,则尝试申请锁tryAcquire(),否则进入3
3、当前时间如果未超时,并且剩余时间大于阈值,则将node的线程挂起,等待被唤醒或超时中断
4、被唤醒后重复申请直到申请成功或线程被中断

排它锁解锁流程分析

调用流程与加锁差不多,通过AQS的release实现,并且自定义了tryRelease():

public void unlock() {
    sync.release(1);
}
protected final boolean tryRelease(int releases) {
    if (!isHeldExclusively()) // 根据持有线程,拦截非法释放操作
        throw new IllegalMonitorStateException();
    // 后续代码,都是有效线程内操作
    int nextc = getState() - releases; // 计算释放后资源的值
    boolean free = exclusiveCount(nextc) == 0; // 计算释放后,排它锁的数量
    if (free) // 如果排它锁数量为0,则清空持有的线程
        setExclusiveOwnerThread(null);
    setState(nextc); // 设置资源state的值
    return free; // 返回排它锁是否还持有
}
// 当前线程是否持有排它锁
protected final boolean isHeldExclusively() {
    return getExclusiveOwnerThread() == Thread.currentThread();
}

由此可知tryRelease()的逻辑很简单,只是操作state的值,以及释放后对 写锁值的检查。

到这里,WriteLock中主要成员已经分析完成。

非公平同步器实现的读锁

定义了一个Sync类型对象sync,由构造函数传入,也就是这里定义了当前写锁是由“公平同步器实现”还是由“非公平同步器实现”实现的。

private final Sync sync;
protected ReadLock(ReentrantReadWriteLock lock) {
    sync = lock.sync;
}

共享锁加锁流程分析

在WriteLock中,加锁方法直接使用的是sync.acquire(1); 追溯起来就是AQS的acquireShared()申请流程。

public void lock() {
    sync.acquireShared(1);
}
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

那么tryAcquireShared()的实现,则是Sync当中了:

protected final int tryAcquireShared(int unused) {
    /*
     * Walkthrough:
     * 1. If write lock held by another thread, fail.
     * 2. Otherwise, this thread is eligible for
     *    lock wrt state, so ask if it should block
     *    because of queue policy. If not, try
     *    to grant by CASing state and updating count.
     *    Note that step does not check for reentrant
     *    acquires, which is postponed to full version
     *    to avoid having to check hold count in
     *    the more typical non-reentrant case.
     * 3. If step 2 fails either because thread
     *    apparently not eligible or CAS fails or count
     *    saturated, chain to version with full retry loop.
     */
    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);
}

同样,方法内有大段的注释,翻译一下:

1、如果存在写锁并被其他线程持有,获取失败
2、读锁是否阻塞、读锁数量是否超过最大值,能否直接修改state
3、如果修改state值成功,根据读锁数量、线程对象等信息,记录一些状态值

其中第二点在修改state时,采用的是compareAndSetState(c, c + SHARED_UNIT),其中c + SHARED_UNIT的含义是:

由于c是当前state的值,它包含读锁(高16位)和写锁(低16位)两部分,如果直接c+1得到的是写锁数量+1,而我们需要对读锁+1时,就要针对高16位进行操作,而SHARED_UNIT=1<<16,它就是读锁的+1标准值。

因此compareAndSetState(c, c + SHARED_UNIT) 的含义类似于compareAndSetState(r, r + 1).

当成个获取到读锁后,需要记录一些信息,在前文我们简单介绍过几个变量及方法,在这里都用到了。

static final class HoldCounter {
    int count = 0; // 数量
    final long tid = getThreadId(Thread.currentThread()); // 线程id
}
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
    public HoldCounter initialValue() {
        return new HoldCounter();
    }
}
Sync() {
    readHolds = new ThreadLocalHoldCounter();
    //...
}
private transient ThreadLocalHoldCounter readHolds;  // 记录线程共享锁信息:HoldCounter
private transient HoldCounter cachedHoldCounter; // 当前操作读锁的计数器
private transient Thread firstReader = null; // 首个获取读锁的线程
private transient int firstReaderHoldCount; // 首个获取读锁的线程的 读锁计数器
static final long getThreadId(Thread thread) {
    return UNSAFE.getLongVolatile(thread, TID_OFFSET); // 获取线程ID
}

回到tryAcquireShared()当中:

if (r == 0) { // 读锁在申请前数量为0
    firstReader = current; // 记录当前线程
    firstReaderHoldCount = 1; // 记录读锁申请数量
} else if (firstReader == current) { // 如果r!=0 并且第一个读锁申请者是当前线程
    firstReaderHoldCount++; // 累加读锁数量
} else {
    HoldCounter rh = cachedHoldCounter; // 获取状态缓存对象
    if (rh == null || rh.tid != getThreadId(current)) // 如果线程id与缓存中不一致
        cachedHoldCounter = rh = readHolds.get(); // 从ThreadLocal中获取当前线程的计数器对象
    else if (rh.count == 0)
        readHolds.set(rh); // 设置rh
    rh.count++; // 累加读锁数量
}

也就是说,共享锁在持有过程中,同步器会记录:

1、持有共享锁的线程、持有的数量
2、首个持有共享锁的线程、持有的数量

在申请到共享锁后,会更新它们的值;猜测释放时,也会更新。

在tryAcquireShared()方法的底部,还有一个fullTryAcquireShared()的调用,如果当前同步队列的头结点不为空、队列中有等待的节点、等待节点申请的锁是排它锁时,那么共享锁的申请需要进行排队,而不是直接申请。而排队申请的处理,就在fullTryAcquireShared()方法中。

final int fullTryAcquireShared(Thread current) {
    /*
     * This code is in part redundant with that in
     * tryAcquireShared but is simpler overall by not
     * complicating tryAcquireShared with interactions between
     * retries and lazily reading hold counts.
     */
    HoldCounter rh = null;
    for (;;) { // 自旋
        int c = getState();
        if (exclusiveCount(c) != 0) { // 存在排它锁
            if (getExclusiveOwnerThread() != current) // 并不是当前线程持有排它锁
                return -1; // 无法申请读锁
        } else if (readerShouldBlock()) { // 读锁申请是否阻塞,也就是同步队列中是否存在写锁申请
            // 如果申请读锁阻塞了,也就是有写锁在前面排队,那么就要考虑中断申请读锁了。
            if (firstReader == current) {
 
            } else {
                if (rh == null) {
                    rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current)) {
                        rh = readHolds.get();
                        if (rh.count == 0)
                            readHolds.remove();
                    }
                    // 清理无效缓存
                }
                if (rh.count == 0) // 如果当前线程读锁计数器为0,说明未申请到锁
                    return -1;
            }
        }
        // 整理、统计 读锁计数器
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
 
        // 这里的else if(xxx)判断,则是整个for(;;)自旋的基础
        if (compareAndSetState(c, c + SHARED_UNIT)) { // 申请读锁资源
            if (sharedCount(c) == 0) { //如果申请前读锁数量为0
                firstReader = current;
                firstReaderHoldCount = 1;
            } else if (firstReader == current) { // 如果首个读锁申请者等于当前线程
                firstReaderHoldCount++;
            } else { // 当前线程并非首个申请读锁的线程,从ThreadLocal对象中获取计数器并更新
                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;
        }
    }
}

当遇到readerShouldBlock时,ReadLock进入自旋方式,尝试申请锁。

共享锁的tryLock

在ReadLock中实现的tryLock()方法,它的主逻辑是由Sync实现的。

public boolean tryLock() {
    return sync.tryReadLock();
}
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;
        }
    }
}

在tryReadLock()方法中,相当于是单独执行了一次tryAcquireShared(),并且只是对state高16位做+1操作。只是不需要考虑共享锁的阻塞情况,也就没有自旋等待过程,这也进一步说明“非公平”的特点。

共享锁带超时的tryLock

带有超时时间的tryLock,实现流程较为复杂,先看一下基本的调用关系:

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}

执行同步器的tryAcquireSharedNanos()方法,具体的会执行tryAcquireShared()尝试直接申请一次锁,如果不成功会执行AQS的doAcquireSharedNanos()。

private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
            }
            nanosTimeout = deadline - System.nanoTime();
            if (nanosTimeout <= 0L)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

这个方法在分析AQS时做过详细分析,这里再写一下粗略的过程:

1、将当前线程封装为node节点,并加入同步队列,计算超时时间点
2、如果node的前置节点是head,则尝试申请锁tryAcquireShared(),设置头节点、唤醒后继节点申请共享锁;否则进入3
3、当前时间如果未超时,并且剩余时间大于阈值,则将node的线程挂起,等待被唤醒或超时中断
4、被唤醒后重复申请直到申请成功或线程被中断

共享锁解锁流程分析

ReadLock的解锁通过AQS的releaseShared()实现,并且自定义了tryRelease():

public void unlock() {
    sync.releaseShared(1);
}
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    if (firstReader == current) { // 判断是否为firstReader,设置计数器        
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else { // 非首次申请读锁的线程,从ThreadLocal中获取计数器并更新
        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; // 高16位减一,得到新的读锁数量
        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; // 当state的值为0时,读锁完全释放(有读锁时不会存在写锁)
    }
}

关注方法中的注释。值得注意的是,读锁的释放,会先修改计数器数值,然后自旋方式去释放state资源。

尾声

初看ReentrantReadWriteLock类,里面复杂的代码很容易放弃,需要学会对类进行拆解,其核心还是利用AQS来达到读写锁既要分离操作,又要互相影响的目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值