Java多线程-17丨JUC-AQS(同步器)源码分析-独占锁的释放

Posted by jiefang on November 7, 2019

JUC-AQS(同步器)源码分析-独占锁的释放

JAVA的内置锁在退出临界区之后是会自动释放锁的, 但是ReentrantLock这样的显式锁是需要自己显式的释放

1
2
3
4
5
6
7
8
Lock lock = new ReentrantLock();
...
lock.lock();
try {
    ...
} finally {
    lock.unlock();
}
  • ReentrantLock的锁释放

锁的释放操作对于公平锁和非公平锁都是一样的,unlock的逻辑并没有放在 FairSyncNonfairSync 里面, 而是直接定义在 ReentrantLock类中:

1
2
3
public void unlock() {
    sync.release(1);
}

AQS.release()

AQS中独占锁的释放源码:

1
2
3
4
5
6
7
8
9
10
11
public final boolean release(int arg) {
    //尝试释放锁,由子类实现
    if (tryRelease(arg)) {
        Node h = head;
        //唤醒后继线程是一个附加操作,无论是否成功唤醒,最后都会返回true
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

ReentrantLock.Sync.tryRelease()方法源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//尝试释放锁,每次调用unlock,则state-1,如果state=0,则设置AQS锁拥有者线程为null,如果锁全部释放完毕,返回true。
protected final boolean tryRelease(int releases) {
    //可能会重入,state-1
    int c = getState() - releases;
    //如果当前调用释放锁的线程不是AQS锁的拥有者,则抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //c=0,锁全部释放完毕
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

AQS.unparkSuccessor()方法源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    //如果head节点的waitStatus小于0,则设为0。
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    //如果后继节点存在且在等待锁,则唤醒;
    //否则,从队列的尾节点开始向前寻找离head最近且waitStatus <= 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);
}