基于 AQS 分析 Condition

90 阅读7分钟

基于 AQS 分析 Condition

崇高的理想就像生长在高山上的鲜花,如果要摘下它,勤奋才能是攀登的绳索

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

代码案例

public class ReentrantLockConditionDemo {
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock();
        Condition condition = reentrantLock.newCondition();
        new Thread(() -> {
            reentrantLock.lock();
            Thread.currentThread().setName("线程一");
            System.out.println(Thread.currentThread().getName() + "执行");
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "执行 await 方法,等待线程二执行完成");
                condition.await();
                System.out.println(Thread.currentThread().getName() + "被唤醒");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }).start();

        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            reentrantLock.lock();
            Thread.currentThread().setName("线程二");
            System.out.println(Thread.currentThread().getName() + "执行");
            try {
                System.out.println(Thread.currentThread().getName() + "执行完成,执行signal方法唤醒线程一");
                condition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
        }).start();

    }
}

结果输出

image.png

源码分析

分析一下构造函数

ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();

看看这句话里面是什么 reentrantLock.newCondition();

public Condition newCondition() {
    return sync.newCondition();
}

一看到sync 又是AQS中东西了,ConditionObject 这个类是AQS中的内部类,但是这个newCondition()方法和newCondition()方法都是ReentrantLock中定义的方法

final ConditionObject newCondition() {
    return new ConditionObject();
}

这个new出来的对象的是类就是AQS中的内部类了

public abstract class AbstractQueuedSynchronizer{  
    public class ConditionObject implements Condition{
        public ConditionObject() { }
    }}

这里面省略了一些东西,不过关系就是这个关系
image.png
在这图里面就能看出来,Node 和 ConditionObject是AQS中的内部类

ConditionObject类里面有什么重要的信息

// 条件队列的第一个节点
private transient Node firstWaiter;
// 条件队列的最后一个节点
private transient Node lastWaiter;

通过这两个属性就知道条件队列它有自己的等待队列,还有一些其他的方法等待分析到的时候再看,先看ReentrantLock加锁的方法,这个之前讲过了 基于AQS 分析 ReentrantLock### 再看看condition.await()这个方法

public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 添加一个等待者到条件等待队列中
    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

分析一下这个方法 addConditionWaiter()

private Node addConditionWaiter() {
    // 定义个t指针,指向条件等待队列的尾部节点,此时为空
    Node t = lastWaiter;
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    // 创建一个Node节点
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

创建一个Node.CONDTION 节点

Node node = new Node(Thread.currentThread(), Node.CONDITION);
Node(Thread thread, int waitStatus) { // Used by Condition
    this.waitStatus = waitStatus;
    this.thread = thread;
}

image.png
firstWaiter 指针指向了这个新的node节点,此时t指针也指向了新的node节点,lastWaiter也指向了该node节点,返回了这个node节点
image.png
该走下面的fullyRelease(node)方法

final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        // 因为前面获取锁来着,所以此时state应该为1
        int savedState = getState();
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}

因为前面获取锁来着,所以此时state应该为1,走到这个方法中 release(savedState)

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

走这个方法尝试着释放锁 tryRelease(arg) arg =1

protected final boolean tryRelease(int releases) { // releases = 1
    // 1 - 1 = 0
    int c = getState() - releases; // 0
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        // 设置当前锁的独占线程为null
        setExclusiveOwnerThread(null);
    }
    // 重新设置state等于0
    setState(c);
    return free;
}

此时state 等于0,exclusiveOwnerThread = null ,所以释放锁成功了,因为这里面就一个线程加锁了,所以AQS中的等待队列的头节点是空值 head==null,因此此时h也是空值,所以直接返回true

int savedState = fullyRelease(node);

锁释放成功后,此时返回的savedState = 1,进入到下面的方法中

while (!isOnSyncQueue(node)) {
    LockSupport.park(this);
    if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
        break;
}

看一下这个关键的地方

final boolean isOnSyncQueue(Node node) {
    // 这个新创建的node的waitStatus 是-1 所以 node.waitStatus == Node.CONDITION 成立,返回了false
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    if (node.next != null) // If has successor, it must be on queue
        return true;
    return findNodeFromTail(node);
}

此时这里返回了false,那么!isOnSyncQueue(node)此时就是true所以这里通过 LockSupport.park(this)将线程挂起

再看看signal方法

同理前面也得获取锁通过ReentrantLock加锁的方式,此时state 就是1了独占线程就是当前线程,先看看signal方法

condition.signal();

public final void signal() {
    // 如果不是当前线程加的锁那么就抛出异常
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    // 获取等待队列的第一个等待者
    Node first = firstWaiter;
    // 此时不为空,所以走 doSignal(first)
    if (first != null)
        doSignal(first);
}

此时不为空,所以走 doSignal(first),看看这个方法里面有什么

private void doSignal(Node first) {
    do {
        // 将firstWaiter 重新赋值到当前等待者的下一个等待着,不过此时等待者就一个,所以first.nextWaiter = null,lastWaiter = null
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        // first.nextWaiter 置于 null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

image.png
由图可知 first.nextWaiter 是 null,走到这个方法里面 !transferForSignal(first)

 final boolean transferForSignal(Node node) {
		// 此时将CONDITION设置为0,成功则返回true
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
		
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

ConditionObject中的等待队列
image.png
接着走enq(node)方法,注意此时是将ConditionObject中的等待队列中的节点,放入到AQS中的等待队列中,好方便下一次获取到锁

private Node enq(final Node node) {
    for (;;) {
        // 此时tail是空值,所以t也是空值,那么初始化队列
        Node t = tail;
        if (t == null) { // Must initialize
            // 创建了一个空的头节点
            if (compareAndSetHead(new Node()))
                // 将尾部节点tail指针也指向了当前创建的头节点
                tail = head;
        } else 
            // 此时t不是空值了因为上一次已经创建出来节点了【头节点】,此时就是将当前的节点的前驱指针指向头节点
            node.prev = t;
            // 在cas操作设置tail的尾部指针
            if (compareAndSetTail(t, node)) {
                // 头节点的next指针指向新加入的节点
                t.next = node;
                return t;
            }
        }
    }
}

此时的AQS中的条件队列
image.png
enq(final Node node)返回的是头节点,因为头节点的waitStatus是0,所以ws > 0 不成立,因此走到了这个方法!compareAndSetWaitStatus(p, ws, Node.SIGNAL) 将头节点的waitStatus设置成了-1。此时就将之前在ConditionObject等待队列中的节点加入到了AQS中的条件队列中了,此时就可以当现在持有锁的线程释放锁后去唤醒在AQS中等待的队列去争抢锁了。

持有锁的线程释放锁之后去唤醒AQS中等待队列

先看看持有锁的线程是怎么释放锁的

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

通过 sync.release(1); 方法此时传进去的值是1

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

再尝试释放锁tryRelease(arg)

protected final boolean tryRelease(int releases) {
    // state -1 此时就是 0了
    int c = getState() - releases;
    // 如果不是当前线程那么就抛出异常
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    // 此时c 等于0
    if (c == 0) {
        free = true;
        // 将独占线程置为null
        setExclusiveOwnerThread(null);
    }
    // 重新设置state =0
    setState(c);
    // 返回true
    return free;
}

定义一个h指针指向了头节点,此时头节点不为空,并且头节点的waitStatus = -1,所以if (h != null && h.waitStatus != 0) 条件成立,走unparkSuccessor(h)方法,并且返回true
image.png

private void unparkSuccessor(Node node) { // 头节点
    // 此时头节点的waitStatus的值为-1
    int ws = node.waitStatus;
    if (ws < 0)
        // 将头节点的waitStatus 设置为0
        compareAndSetWaitStatus(node, ws, 0);
    // 定义一个s指针指向头节点的下一个节点
    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);
}

image.png
因为s!=null,并且s的waitStatus = 0 所以通过LockSupport.unpark(将线程唤醒),此时唤醒之后接着当时挂起地方继续往下执行,想想在哪里挂起的
image.png
再看看!isOnSyncQueue(node) 这个方法

final boolean isOnSyncQueue(Node node) {
    // 此时node的waitStatus是0不是Node.CONDITION ,并且 node.prev也不是null
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    // 此时的node.next是null
    if (node.next != null) // If has successor, it must be on queue
        return true;
    // 所以走到了这里
    return findNodeFromTail(node);
}

我们再看看这个findNodeFromTail(node)方法

private boolean findNodeFromTail(Node node) {
    // 定义一个t指针指向了AQS队列中的尾部节点
    Node t = tail;
    for (;;) {
        // t 是 node所以返回了true
        if (t == node)
            return true;
        if (t == null)
            return false;
        t = t.prev;
    }
}

image.png
那么此时就退出了while循环了,走下面的逻辑代码了

if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
    interruptMode = REINTERRUPT;

又回到了我们熟悉的获取锁的流程了acquireQueued(node, savedState)

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);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

看看这个tryAcquire(arg)方法

protected final boolean tryAcquire(int acquires) {
    return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
    // 获取当前线程
    final Thread current = Thread.currentThread();
    // 此时state 是0,那么c就是0
    int c = getState();
    if (c == 0) {
        // 设置state为1 ,设置独占线程并且返回true
        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;
}

此时就该走下面的修改指针的逻辑了

setHead(node);
p.next = null; // help GC
failed = false;

看看setHead里面是什么东西

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

image.png
到这里其实就是这个线程已经获取到锁了,就可以继续往下执行逻辑了