AQS-Condition源码学习

Condition

在使用synchronized对程序时, 当一个线程获取锁成功后, 在synchronized代码块中调用wait方法后,会将线程放入等待队列中,其他线程调用notify(或notifyAll)方法后才会将当前线程唤醒,进入同步队列,而notifyAll是将等待队列中的所有线程都放入同步队列中,notify是挑选其中一个线程进入同步队列

这种原始的通知方法不能够指定通知某个线程,为了达到精准通知,因此JDK5后引入了AQS,而AQS中包含一个ConditionObject的内部类(我们经常说的Condition)

  • Condition是由Lock对象的newCondition创建,依赖于Lock对象

  • ConditionObject是AQS的内部类,实现Condition接口,每个Condition对象都包含着一个等待队列, 等待队列依然是使用AQS中的Node节点实现, 这里的等待队列是一个单向队列, 一个同步器可以有多个等待队列

基本结构如下:

源码分析
await()
public final void await() throws InterruptedException {
    // 判断当前线程是否中断
    if (Thread.interrupted())
        throw new InterruptedException();
    // 将当前线程添加到等待队列中
    Node node = addConditionWaiter();
    // 释放当前线程的锁资源,会unpark head 的后续线程
    int savedState = fullyRelease(node);
    // 记录中断的标记
    int interruptMode = 0;
    // isOnSyncQueue: 判断node的waiterStatus是否死condition,以及node。pre是否为空
    while (!isOnSyncQueue(node)) {
        // 阻塞当前线程
        LockSupport.park(this);
        // checkInterruptWhileWaiting: 检查是在signal前,还是后发生的中断
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // node 所属线程被唤醒后,继续向下执行
    // acquireQueued: 尝试获取锁,直到满足条件
    // interruptMode: 中断状态是否被修改过
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    // 如果node有next, 那么将concelled的节点从等待队列中删除
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    // != 0 说明有中断发生,进行中断处理
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
addConditionWaiter
private Node addConditionWaiter() {
    // 最开始lastWaiter为空
    Node t = lastWaiter;
    // 如果waitStatus 不是condition状态,那么清除掉非condition状态的节点
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    // 为当前线程创建condition状态的节点
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}
isOnSyncQueue:

返回节点是否已经在同步队列中

final boolean isOnSyncQueue(Node node) {
    // 如果node已经进入同步队列,那么waitStatus为0, 且pre不为null
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    // next != null, 说明node并不是在同步队列最后一个
    if (node.next != null) // If has successor, it must be on queue
        return true;
    return findNodeFromTail(node);
}
// 寻找同步队列中是否存在node节点
private boolean findNodeFromTail(Node node) {
    Node t = tail;
    for (;;) {
        if (t == node)
            return true;
        if (t == null)
            return false;
        t = t.prev;
    }
}
Signal
public final void signal() {
    // 如果不是拥有锁的线程调用这个方法将抛出异常
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}

private void doSignal(Node first) {
    // transferForSignal: 将等待队列第一个转移到同步队列中
    do {
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

final boolean transferForSignal(Node node) {
 	// 如果待转移的节点waitStatus不是condition,那么可能已经被其他线程转移到同步队列中
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

  	// 将node添加到同步队列中
    Node p = enq(node);
    int ws = p.waitStatus;
	// ws > 0 说明被其他线程取消了,
    // CAS: 将node节点状态修改为SIGNAL, 用于前序节点唤醒他
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}
signalAll
  • 唤醒等待队列的所有线程, 只是一个Condition等待队列
public final void signalAll() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignalAll(first);
}

private void doSignalAll(Node first) {
    lastWaiter = firstWaiter = null;
    // 循环转移每个结点到同步队列中
    do {
        Node next = first.nextWaiter;
        first.nextWaiter = null;
        transferForSignal(first);
        first = next;
    } while (first != null);
}
大致流程:

参考:

《Java并发编程的艺术》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值