AQS源码分析之ConditionObject

简介

传统的内置锁都只能有一个相关联的条件队列,因此多个线程可能在同一个条件队列上等待不同的条件谓词,导致使用notify时导致信号消失,或者使用notifyAll唤醒了非等待该信号类型的线程,造成了极大的开销。

因此,可以使用显式的Lock和Condition而不是内置锁和条件队列,来编写一个带有多个条件谓词的并发对象。在每个锁上可存在多个等待、条件等待可以是可中断的或不可中断的、基于限时的等待、以及公平的或非公平的队列操作。

与内置条件队列不同的是,对于每个Lock,可以由任意数量的Condition对象。Condition对象继承了相关的Lock对象的公平性,对于公平的锁,线程会按照FIFO顺序从Condition.await中释放。

public class ConditionBoundedBuffer<T> {
    protected final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    @SuppressWarnings("unchecked")
    private final T[] items = (T[]) new Object[19];
    private int tail, head, count;

    public void put(T x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await();
            }
            items[tail] = x;
            if (++tail == items.length) {
                tail = 0;
            }
            ++count;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await();
            }
            T x = items[head];
            items[head] = null;
            if (++head == items.length) {
                head = 0;
            }
            notFull.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
}

通过将两个条件谓词分开并放到两个等待线程集中,Condition使其更容易满足单次通知的需求,signal比signalAll更高效,能够极大减少在每次缓存操作中发生的上下文切换与锁请求的次数。

与内置锁和条件队列一样,但使用显式的Lock和Condition时,也必须满足锁、条件谓词和条件变量之间的三元关系。在条件谓词中包含的变量必须由Lock来保护,并且在检查条件谓词以及调用await和signal时,必须持有Lock对象。

Condition接口


public interface Condition {

     /** 
      * 暂停此线程直至一下四种情况发生
      * 1.此Condition被signal()
      * 2.此Condition被signalAll()
      * 3.Thread.interrupt()
      * 4.伪wakeup
      * 以上情况.在能恢复方法执行时,当前线程必须要能获得锁
      */
    void await() throws InterruptedException;


    //跟上面类似,不过不响应中断
    void awaitUninterruptibly();

    //带超时时间的await(),并响应中断
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //带超时时间的await()
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    //带deadline的await()
    boolean awaitUntil(Date deadline) throws InterruptedException;

    //唤醒某个等待在此condition的线程
    void signal();

    //唤醒有等待此condition的所有线程
    void signalAll();
}

AQS的ConditionObject

ConditionObject的实现其实就是维护了两个队列

  1. condition队列:表示等待条件队列,其waitStatus=Node.CONDITION,由firstWaiterlastWaiter两个舒心控制。
  2. sync队列:表示可以竞争锁的队列,这个跟AQS的独占锁队列一致,与ReentrantLock独占锁共用一个队列。

await()方法分析

该方法就是把当前线程创建一个Node加入condition队列,释放当前占有的锁之后,接着就一直循环,判断当前节点在不在sync队列队列中,如果不在,则当前线程挂起;否则,就可以重新竞争独占锁。

public final void await() throws InterruptedException {
    /*如果当前线程已经被中断*/
    if (Thread.interrupted())
        throw new InterruptedException();
    Node node = addConditionWaiter();
    /*释放当前线程持有的锁*/
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    /*通过循环,查看当前线程是否已经移入到sync队列中*/
    while (!isOnSyncQueue(node)) {
        /*如果没有在sync队列,那么将当前线程挂起*/
        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);
}

添加一个等待Node到条件队列中

private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.

    /*如果最后一个等待节点的等待状态不是 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;
}

释放当前的state,最终还是调用tryRelease方法

final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        /*获取当前的状态值,通过release方法释放锁*/
        int savedState = getState();
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}

判断是否在同步队列中

final boolean isOnSyncQueue(Node node) {
    /*如果状态为CONDITION,说明还在condition队列中,还得循环等待*/
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    /*当执行signal后,会执行enq方法,将node加入到队列中去*/
    /*有前驱节点也有后置节点,那么一定在sync队列中*/
    if (node.next != null) 
        return true;
    /*
     * 其前置节点为非null,但是也不在Sync也是可能的,
     * 因为CAS将其加入队列失败.所以我们需要从尾部开始遍历确保其在队列
     */
    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()方法分析

该方法就是把条件队列中头结点移动到sync队列中

public final void signal() {
    /*如果不是独占锁模式,则抛出 非法监视器异常*/
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();

    //将condition队列的第一个节点出队列,并将其加入AQS的锁sync队列
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}

释放信号

private void doSignal(Node first) {
    do {
        /*将firstWaiter指向下一个节点
        * 如果下一个节点为空,则将lastWaiter置为空
        * */
        if ((firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;

        /*断开头结点与其他节点的连接*/
        first.nextWaiter = null;
    } while (!transferForSignal(first) && (first = firstWaiter) != null);
}

将一个节点从condition队列转换到Sync队列

final boolean transferForSignal(Node node) {
    /*
     * 所谓的condition队列和sync队列就在于waitStatus的值
     * 在这里将waitStatus改为NORMAL,如果不能改变,说明当前节点已经被取消(CANCELLED),
     * 那么重新尝试下一个节点
     */
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    /*
     * 将当前节点移动到sync队列中,接着设置前驱节点等待状态,
     * 如果被取消或者设置状态失败,那么唤醒该线程,让该线程重新同步,为了确保waitStatus是无害的
     */
    Node p = enq(node);/*当前节点的前驱节点*/
    int ws = p.waitStatus;
    /*如果前驱节点取消,或修改状态失败*/
    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);
}

遍历所有节点,使其加入到Sync队列

private void doSignalAll(Node first) {
    lastWaiter = firstWaiter = null;
    do {
        Node next = first.nextWaiter;
        first.nextWaiter = null;
        transferForSignal(first);
        first = next;
    } while (first != null);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值