Java 并发编程之 AQS ReentrantLock 公平锁源码解析

Java juc AQS 大约 1782 字

部分源码

java.util.concurrent.locks.ReentrantLock.FairSync

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    @ReservedStackAccess
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

java.util.concurrent.locks.AbstractQueuedSynchronizer#hasQueuedPredecessors

public final boolean hasQueuedPredecessors() {
    Node h, s;
    if ((h = head) != null) {
        if ((s = h.next) == null || s.waitStatus > 0) {
            s = null; // traverse in case of concurrent cancellation
            for (Node p = tail; p != h && p != null; p = p.prev) {
                if (p.waitStatus <= 0)
                    s = p;
            }
        }
        if (s != null && s.thread != Thread.currentThread())
            return true;
    }
    return false;
}

公平锁会在每次上锁前都会去检查一下CLH队列的头节点,判断是否为空,且头节点中的thread字段是否是当前线程。

头节点的thread的字段初始化时为空,解锁时也会将出队列节点的thread字段置空。

第一次lock时,头节点还没初始化,所以为null,直接上锁成功。

锁重入时,需判断s.thread != Thread.currentThread()

所以公平锁抢得锁的时机只有在一开始没有竞争的情况下,如果有竞争了,其他线程都会进入CLH队列。

阅读 38 · 发布于 2021-10-05

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批