AbstractQueuedSynchronizer源码分析(二)---解锁

62 阅读3分钟

前边一篇文章分析了ReentrantLock通过FairSync实现的加锁,现在继续分析解锁的源码。

public void unlock() {
    sync.release(1);
}
// 这个是AbstractQueuedSynchronizer的方法
public final boolean release(int arg) {
    // 首先看tryRelease方法。
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
protected final boolean tryRelease(int releases) {
    // 获取一下当前锁的状态,然后减去1
    int c = getState() - releases;
    // 当前线程必须持有锁,才可以解锁
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 如果等于0了,则解锁成功,重置锁,并设置排它锁持有为null
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

public final boolean release(int arg) {
    // 接着看,释放锁成功了。则去唤醒阻塞队列的第一个节点。
    if (tryRelease(arg)) {
        // 先把head节点拿到。
        Node h = head;
        // head节点不能为空(当前线程就是属于head,并且head的waitStatus被它的下一节点修改为-1了的。如果这个地方有疑问的可以看--# [AbstractQueuedSynchronizer源码分析(一)---加锁](https://juejin.cn/post/7216299104404946999))
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h); // 此时的参数是head节点
        return true;
    }
    return false;
}
// Node参数是head节点
private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    // 修改head的waitStaus=0,此时是-1
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
     // s就是阻塞队列的第一个节点
    Node s = node.next;
    // 阻塞队列为空或者第一个节点取消了排队
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 为什么从最后一个节点往前遍历,寻找下一个节点?是因为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);// 唤醒下一个Node。
}

接下来就是线程从加锁被挂起的地方重新开始执行了

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);// 唤醒线程
    return Thread.interrupted();// 判断是否被中断了。false
}
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            // 重新进入循环,p是等于head的,然后尝试加锁,成功
            if (p == head && tryAcquire(arg)) {
                setHead(node);//设置当前节点为head,返回
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                //1. parkAndCheckInterrupt()方法是返回false的,if判断不会进去,然后重新循环
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        // 正常情况不会执行
        if (failed)
            cancelAcquire(node);
    }
}
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        // acquireQueued方法返回了false,获取锁成功了,线程继续执行自己的业务代码。
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

顺道分析一下非公平锁的加锁源码吧。

final void lock() {
    // 由于非公平锁不需要按照队列一个个执行,所以进来就先判断是否可以后去所,如果成功了,则万事大吉。
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        // 获取锁失败了。
        acquire(1);
}
public final void acquire(int arg) {
     还是先try一下吧。
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
    // 此时调用的就是非公平锁的方法了.
    return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 不一样的地方来了,在公平锁中,如果锁是空着的,会先去阻塞队列中看看有没有别的线程排着的。如果有的,当前线程是不可以直接获取锁的,要去排队,但是非公平锁就不会管这么多,直接开抢。
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
public final void acquire(int arg) {
    // 如果获取锁失败了,还是老老实实的去排队去吧。
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

再来看看非公平锁的解锁代码又是怎么样操作的?和公平锁一样了。

public final boolean release(int arg) {
    // 任然是先去解锁
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            // 然后去唤醒下一个节点。
            unparkSuccessor(h);
        return true;
    }
    return false;
}

总结:看了源码就会发现,非公平锁和公平锁只有在lock的时候有一点点区别,非公平锁在lock的时候,会先去抢一下锁,如果没有抢到,才会进入acquire(1);,在进入这个方法了,如果发现了锁释放了,还是会直接抢锁,而不去判断阻塞队列里边是否有线程等着了。如果这里也还是没有抢到锁,那剩下的就和公平锁一样了。

参考文档