jdk读写锁ReentrantReadWriteLock

public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

 

构造方法中会生成读锁和写锁。

 

 

其sync中通过一个32位数字state表示当前持有读写锁的数量。

static final int SHARED_SHIFT   = 16;
static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 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; }

 

高16位表示读锁的数量,低16位表示写锁的数量。通过算数右移state16位表示读锁的数量,将state与1左移16位-1的结果按位相与的结果为当前写锁的数量。

 

 

加读锁

public void lock() {
    sync.acquireShared(1);
}

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

 

加读锁的时候调用sync的acquireShard()方法,实现在了父类aqs中,在其中,先尝试调用tryAcquireShard()方法。

 

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);
}

 

这里首先得到当前state判断当前是否已经存在别的线程获取写锁,如果有,直接返回-1。否则再根据state得到当前的读锁数量,之后判断当前的读锁是否需要阻塞,在公平锁的情况下,如果存在比当前线程更早进入的aqs并阻塞的线程,那么就需要等待,在非公平锁的情况下,则判断当前aqs队列中的线程是否是独占模式(试图加写锁的节点)来判断需不需要阻塞。

 

如果不需要阻塞,并且当前的读锁的数量小于上限,则通过cas给state的读锁数量加上一,如果这是第一个加读锁的线程,则当前线程记为第一个读者firstReader,并给当前第一个读者所加锁数量加一。每个线程持有的读锁数量通过threadLocal来保存,这里给threadLocal中保存的HoldCounter中的count加上一,并加入threadLocal管理。

之后返回1,表示读锁的加入并没有被阻塞。

否则,则调用fullTryAcquireShard()继续试图获得读锁。

final int fullTryAcquireShared(Thread current) {
    HoldCounter rh = null;
    for (;;) {
        int c = getState();
        if (exclusiveCount(c) != 0) {
            if (getExclusiveOwnerThread() != current)
                return -1;
            // else we hold the exclusive lock; blocking here
            // would cause deadlock.
        } else if (readerShouldBlock()) {
            // Make sure we're not acquiring read lock reentrantly
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
            } 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)
                    return -1;
            }
        }
        if (sharedCount(c) == MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        if (compareAndSetState(c, c + SHARED_UNIT)) {
            if (sharedCount(c) == 0) {
                firstReader = current;
                firstReaderHoldCount = 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;
        }
    }
}

 

这个方法中,会不断循环,如果当前已经存在别的线程持有写锁,那么直接返回-1。之后再一次判断当前加读锁是否需要阻塞,如果需要阻塞,则继续判断该线程是否已经持有过读锁,如果没有,则也返回-1。如果此时已经不需要阻塞,则和之前的操作一样加上获取读锁。

 

 

如果成功获取读锁的情况下,返回的1,回到aqs的acquireShard()方法,则会直接结束,代表读锁的成功获取,如果返回-1,说明此时已经有别的线程持有写锁或者当前加读锁需要阻塞,并且当前线程并未曾经成功获取读锁。那么则调用doAcquireShard()方法将当前线程加入队列并挂起。

private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

 

这里的代码实现在了aqs中。

 

这里的线程作为share节点加入到队列中并被挂起,当被唤醒的时候会重新调用之前的tryAcquireShard()方法重新试图取得读锁。

 

 

加写锁

public void lock() {
    sync.acquire(1);
}

 

acquire()还是实现在aqs中。

 

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

 

首先tryAcquire()实现在了sync中,试图获取写锁。

 

protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    int w = exclusiveCount(c);
    if (c != 0) {
        // (Note: if c != 0 and w == 0 then shared count != 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;
}

首先判断state是否为0,如果非0,则说明已经存在写锁或者读锁,如果当前不存在读锁,或者当前线程并没有持有写锁,那么直接返回false。

否则如果当前线程已经持有写锁,直接给state加上相应的数量即可,并返回true。

之后判断当前加写锁是否需要阻塞,在非公平锁中,加写锁永远不会被阻塞,在公平锁中的判断方式与加读锁一样。

如果不需要阻塞,直接通过cas改变state,并将当前线程作为持有写锁的线程并返回true。

 

之后回到aqs的acquire()方法,在刚才的tryAcquire()方法中,如果当前已经存在别的线程持有读锁或者写锁,或者在公平锁的情况下aqs队列中已经存在线程等待,那么则会返回false,加读锁失败,构造独占节点加入aqs队列中挂起等待唤醒。

 

相比加锁,解除锁的步骤就很简单,并且大部分操作都在aqs中完成。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值