并发源码解析(三)--AQS条件队列

本文详细介绍了AQS中的条件队列,每个锁可以有多个条件变量,每个条件变量维护一个单链表条件队列。ConditionObject作为内部类实现Condition接口,并提供了等待和唤醒方法。await()将线程加入条件队列,而signal或signalAll()方法则将线程从条件队列转移到AQS阻塞队列,等待获取锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、条件队列

AQS的一个锁可以对应多个条件变量。使用时先使用lock.newCondition()来new出来一个在AQS内部声明的ConditionObject对象,ConditionObject是AQS的内部类,可以访问AQS内部的变量和方法。而在每个条件变量内部都维护了一个条件队列,用来存放调用条件变量的await()方法时被阻塞的线程。

这个条件队列和AQS队列不是一样东西!

二、源码分析

2.1 ConditionObject内部类的继承关系

在这里插入图片描述
在这里插入图片描述
可以看出ConditionObject实现了Condition接口

2.2 Condition接口

public interface Condition {

    /**
     *  线程进入等待状态直到被通知或中断
     */
    void await() throws InterruptedException;

    /**
     * 当线程进入等待状态直到被通知,该方法对中断不敏感
     */
    void awaitUninterruptibly();

    /**
     * 当前线程进入等待状态直到被通知、中断或者超时(纳秒)。返回值为剩余的时间
     */
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    /**
     * 当前线程进入等待状态直到被通知、中断或者超时
     */
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    /**
     * 线程进入等待状态直到被通知、中断或者到某个时间
     */
    boolean awaitUntil(Date deadline) throws InterruptedException;

    /**
     * 唤醒一个在Condition上的线程
     */
    void signal();

    /**
     * 唤醒所有等待在Condition上的线程
    void signalAll();
}

2.3 ConditionObject内部类

因为Conditon的操作需要获取相关联的锁,所以作为同步器AQS的内部类。每个Condition对象都包含了一个等待队列。

2.3.1 等待队列

Condition等待队列的节点Node和AQS等待队列的节点是一样的

/** 头节点. */
private transient Node firstWaiter;
/** 尾节点. */
private transient Node lastWaiter;

Condition的等待队列不像AQS那样是双链表,Condition的是单链表队列。lock可以拥有多个条件队列。

2.3.2 等待方法

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

加入条件队列的代码

private Node addConditionWaiter() {
	//获得等待队列的尾节点
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
	//队尾节点部位null,等待状态不是CONDITION
    if (t != null && t.waitStatus != Node.CONDITION) {
    	//清除节点,其实就是链表删除,不详细说了
        unlinkCancelledWaiters();
        //再次获取尾节点
        t = lastWaiter;
    }
    //封装当前线程,等待状态到节点中
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    //如果t为null,队列为空
    if (t == null)
        firstWaiter = node;
    else
    	//否则将入队尾
        t.nextWaiter = node;
    //队尾指向新加入的节点
    lastWaiter = node;
    //返回新节点
    return node;
}

节点已加入条件队列,开始释放锁

//释放锁
final int fullyRelease(Node node) {
    boolean failed = true;
    try {
    	//获得同步状态
        int savedState = getState();
        //使用当前状态去释放锁
        if (release(savedState)) {
            failed = false;
            //成功后返回当前同步状态
            return savedState;
        } else {
        	//失败就抛出异常
            throw new IllegalMonitorStateException();
        }
    } finally {
    	//确保不成功就将该节点设置为取消状态
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}

public final boolean release(int arg) {
	//tryRelease要子类自己实现
    if (tryRelease(arg)) {
    	//获取阻塞队列的头节点
        Node h = head;
        if (h != null && h.waitStatus != 0)
        	//unpark后继节点
            unparkSuccessor(h);
        return true;
    }
    return false;
}

    private void unparkSuccessor(Node node) {
        /*
         * 传入的node节点,此时为头结点,waitStatus小于0则置0
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * 找到头节点后的第一个排队节点(等待状态<=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)
        	//让s节点的thread获取线程许可证
            LockSupport.unpark(s.thread);
    }

进行条件等待,传入的是当前线程

   /**
     * 判断节点是否在同步队列中
     */
    final boolean isOnSyncQueue(Node node) {
    	//等待队列无prev节点
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        //等待队列的后继节点是nextWaiter
        if (node.next != null) // If has successor, it must be on queue
            return true;
        /*
         * node.prev can be non-null, but not yet on queue because
         * the CAS to place it on queue can fail. So we have to
         * traverse from tail to make sure it actually made it.  It
         * will always be near the tail in calls to this method, and
         * unless the CAS failed (which is unlikely), it will be
         * there, so we hardly ever traverse much.
         * 上面无法判断,再去阻塞队列中判断
         */
        return findNodeFromTail(node);
    }

2.3.3 唤醒方法

public final void signal() {
	//判断当前线程是否持有锁
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    //获得第一个等待线程
    Node first = firstWaiter;
    //如果条件队列有排队者
    if (first != null)
    	//唤醒条件队列的头节点
        doSignal(first);
}

//唤醒条件队列的头节点
private void doSignal(Node first) {
    do {
    	//将等待队列的头节点移动至first的后继节点,如果此时头节点为空,则条件队列也为空
        if ( (firstWaiter = first.nextWaiter) == null)
        	//尾节点指针也置空
            lastWaiter = null;
        //first的nextWaiter引用置null,要拿去阻塞队列了,跟条件队列没关系了
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

final boolean transferForSignal(Node node) {
    /*
     * 如果失败则是该节点被取消了
     */
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    /*
     * 将节点加入阻塞队列
     */
    //同独占锁入队
    Node p = enq(node);
    //返回p的前置节点
    int ws = p.waitStatus;
    //如果ws > 0 被取消 或 修改状态失败则直接唤醒node节点线程
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
    	//获取线程许可证,获取到锁或被挂起
        LockSupport.unpark(node.thread);
    return true;
}

三、总结

以上是条件队列的内容。

首先要明白一个锁只有一个AQS阻塞队列,但是可以有多个条件变量,每个条件变量有自己的一个条件队列。

await()方法会使该线程放入条件变量的条件队列中,当另外线程调用条件变量的signal或signalAll()方法时,线程会从条件队列中出队,移动到AQS的阻塞队列中,等待获取锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值