ReentrantLock是如何基于AQS实现的(3)

AbstractQueuedSynchronizer#unparkSuccessor
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
    }

unparkSuccessor见名知意适用于接触后面节点的阻塞状态。整个方法的逻辑就是找到传入节点的后继节点,将其唤醒(排除掉状态为cancel即waitStatus > 0的节点)。

公平锁和非公平锁

ReentrantLock的构造方法接受一个可选的公平参数。当设置为 true 时,在多个线程的竞争时,倾向于将锁分配给等待时间最长的��程。
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

在多个锁竞争统一资源的环境下,AQS维护了一个等待队列,未能获取到锁的线程都会被挂到该队列中。如果使用公平锁则会从队列的头结点开始获取该资源。

而根据代码在公平锁和非公平锁的实现的差别仅仅在于公平锁多了一个检测的方法。

公平锁
protected final boolean tryAcquire(int acquires) {
    //...
    if (c == 0) {
        if (!hasQueuedPredecessors() //!hasQueuedPredecessors()便是比非公平锁多出来的操作
        && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //...
    return false;
}

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

方法逻辑很简单,就是如果等待队列还有节点并且排在首位的不是当前线程所处的节点返回true表示还有等待更长时间的节点。需要等这部分节点获取资源后才能获取。

Linux公社的RSS地址https://www.linuxidc.com/rssFeed.aspx

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c5a9fd4c35c255e29d7a64eebf9138e2.html