ArrayBlockingQueue 与 AQS中的ConditionObject

BlockingQueue是我们在使用线程池的时候使用比较多的等待队列,这里同时借助BlockingQueue分析下AQS中的ConditionObject。

ArrayBlockingQueue

构造函数 :

    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        // 构造函数中会new出一个新的ReentrantLock 方便后续使用
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition(); // 用于挂起生产节点
        notFull =  lock.newCondition(); // 用于挂起消费节点
    }

put方法:

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        // 这里加锁保证入队的原子性
        // 由于使用Interruptibly结尾的lock 所以会抛出中断异常
        lock.lockInterruptibly(); 
        try {
            while (count == items.length)
                notFull.await(); // 队列已满 阻塞自己
            enqueue(e); 入队
        } finally {
            lock.unlock();
        }
    }

    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal(); // 唤醒一个消费节点
    }

poll:

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            // 如果 队列为空 直接返回空  否则取出一个
            return (count == 0) ? null : dequeue(); 
        } finally {
            lock.unlock();
        }
    }

    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0; // 将takeIndex 重置为队列头部
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal(); // 唤醒一个生产节点
        return x;
    }

take:

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await(); // 如果队列为空 会阻塞自己
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

可以看到,在BlockingQueue中,使用Condition做了一些阻塞操作,下面来分析下:
首先newCondition方法会生成一个ConditionObject对象,该对象是AQS中的一个内部类:

    public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;
    }

在使用condition时,AQS会维护一个ConditionObject队列,队列中记录了所有正在等待的节点,并且这些节点不会去抢锁。
然后来看下await方法,该方法的作用是将当前线程放入等待队列,并从CLH队列中取出(关于CLH队列,其实就是AQS中维护的双向链表,用于等待获取锁):

    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 新建condition状态的节点并将其入队
        AbstractQueuedSynchronizer.Node node = addConditionWaiter();
        // 释放当前节点的锁
        // 注意 这里会记录下拿了几个锁 后面加锁也需要同样的数量
        int savedState = fullyRelease(node); 
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) { // 节点没有在CLH队列中里面 
            // //将线程进行挂起,前面已经释放掉锁了,并且已经安全的添加到了condition队列中
            LockSupport.park(this);
            //  执行这里的条件: 被中断 or 被前置节点唤醒
            // 这里只要checkInterruptWhileWaiting 返回的是0 就继续park
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        //  重新抢锁 此时节点已经在CLH队列中了 获取成功后判断该线程是否发生错误
        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.
        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;
    }

    final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        // 注意 这里有个next 有个 nextWaiter 
        // 一个用于CLH队列 一个用于condition等待队列 要区分开来
        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);
    }
    // 获取队列尾节点
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) { // 从尾部遍历整个节点 看是否有当前节点
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }

释放当前节点的所有锁:

    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) {
        if (tryRelease(arg)) { // 释放所有锁
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); // 唤醒后继节点
            return true;
        }
        return false;
    }

清理队列中的无效节点:

    private void unlinkCancelledWaiters() {
        AbstractQueuedSynchronizer.Node t = firstWaiter;
        AbstractQueuedSynchronizer.Node trail = null;
        while (t != null) {
            AbstractQueuedSynchronizer.Node next = t.nextWaiter; // 拿到下一个节点
            // 若头节点的状态已经不是CONDITION 
            if (t.waitStatus != AbstractQueuedSynchronizer.Node.CONDITION) {
                t.nextWaiter = null; // 剔除头节点
                if (trail == null)
                    firstWaiter = next; // 直接将 firstWaiter 记录为 next
                else
                    // trail已经被记录为CONDITION状态的节点
                    // 将nextWaiter 记录为next 即:
                    // CONDITION -> CANCELED -> UNKNOW
                    // 转换为 CONDITION -> UNKNOW
                    trail.nextWaiter = next; 
                if (next == null) // 已经遍历到了队尾
                    lastWaiter = trail;
            }
            else
                trail = t; // 如果当前节点还是CONDITION状态 则使用trail记录下
            t = next;
        }
    }

该方法中有个疑点,CONDITION状态是什么时候被重置掉的 ?
其实是在await方法中 :

// 这里 线程被唤醒后 会执行checkInterruptWhileWaiting 方法 
 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;


    private int checkInterruptWhileWaiting(AbstractQueuedSynchronizer.Node node) {
        //  判断节点是否还在condition队列里面,如果在,将状态变成0,放到等待返回true,抛异常。
        return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
    }

    final boolean transferAfterCancelledWait(Node node) {
        // 也就是在这里 node 状态会被重置为0
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
            enq(node); // 将该节点入队
            return true;
        }
        /*
         * If we lost out to a signal(), then we can't proceed
         * until it finishes its enq().  Cancelling during an
         * incomplete transfer is both rare and transient, so just
         * spin.
         */
        // 可能有别的线程通过signal 唤醒了当前节点
        // 并且正在入队 那么这时 自旋啥也不干
        while (!isOnSyncQueue(node)) 
            Thread.yield();
        return false; // 修改node 状态失败 返回false
    }

     // 这个方法应该很熟悉了 将一个节点放入CLH队列中
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

接下来看下signal方法:

    public final void signal() {
        // getExclusiveOwnerThread() == Thread.currentThread(); 
        // 持有锁的线程是否是本线程,如果不是持有锁的线程直接抛异常
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        AbstractQueuedSynchronizer.Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }

    private void doSignal(AbstractQueuedSynchronizer.Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null) // 将头节点后移一位
                lastWaiter = null; // 队列已经空了
            first.nextWaiter = null; // 断开当前节点对后继节点的引用
        } while (!transferForSignal(first) &&
                (first = firstWaiter) != null); // 队列不为空 则继续
    }

    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        // 将剥离出来的节点改为0状态
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) 
            return false; // 失败的话 直接返回 操作等待队列中下一个节点

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node); // 将当前节点入队 注意 这里返回的是当前节点的前驱节点
        int ws = p.waitStatus;
        // 如果前置节点状态大于0(被取消)
        // 或者更新状态为SIGNAL 失败(SIGNAL表示后继节点可以被唤醒)
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            // 则直接唤醒当前线程
            LockSupport.unpark(node.thread);
        return true;
    }

signalAll:

        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); // 主要这里不同 只要等待队列还有节点 就继续唤醒
        }

你可能感兴趣的:(ArrayBlockingQueue 与 AQS中的ConditionObject)