AbstractQueuedSynchronizer 源码分析(二)

 

AQS 上一篇分析

接着上次分析,这次把ConditionObject 功能分析下

 

当我们使用BlockingQueue的时候,消费者端从队列里获取过元素发现为空,当前线程就会await。当生产者把数据put到Queue时候被阻塞的线程将被唤醒。这种情况下的线程排队等待和被选唤醒就是由ConditionObject这个类来管理的

 

我们先来看下ArrayBlockingQueue的源码:

    //消费者获取数据
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

    //生产者放数据
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

    //唤醒等待的线程
     private void enqueue(E x) {
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

 

这里要注意下,notEmptyCondition或是NotfullCondition都是和一个Lock实例关联的(由某个锁创建出来的对象)。当线程被唤醒时也不是直接去执行,因为他需要先获取到关联的锁才可以去执行。这种转换我们称为条件等待队列(等待条件满足唤醒)转 同步等待锁队列(获取锁排队)。

 

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

 1.调用addConditionWaiter方法,增加条件链表节点。这个节点和之前说的Node一样,也是把当前线程封装,关联节点关系,一会儿我们进去详细看下。

2.调用fullyRelease方法,把当前线程的锁释放掉。避免其他线程获取不到锁。

3.判断是否在同步等待的队列中(获取锁的等待队列),如果不在调用LockSupport.park当前线程进入阻塞。

4.在循环里判断是线程是否被中断。如果中断,修改节点的状态,方便后面清理节点。

5.如果在同步等待队列中,就会执行acquireQueue方法尝试获取锁(之前分析的流程)。

6.如果线程等待过程中被打断,向上抛出异常。

 

看下addConditionWaiter方法:

        private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

这块代码比较简单,就是把线程封装成条件节点,加入到条件队列。注意这里是用nextWaiter字段委托单向链表的,并没有用next,next是关联同步队列链表节点的。

 

fullyRelease 释放锁方法:

    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 void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }

1.isheldExclusively是判断当前线程是否是持有线的线程。

2.从第一个节点唤醒。

        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

调用transferForSignal方法,如果返回false说明first节点唤醒失败,则尝试唤醒链表的下一个节点。

 

    final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        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;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

1.调用enq方法把当前要唤醒的节点加入到同步队列中(竞争锁的队列)。注意,返回的p是当前节点的上一个节点。

2.判断上一个节点的状态是取消直接唤醒当前线程,或修改waitStatus为signal失败也直接唤醒线程。都不成立就代表唤醒成功,会结束上一步的while.完成队列的转换操作。

注意:这里读者要思考下,如果在这里直接唤醒了node线程他的执行位置在哪。如果没唤醒,下次被唤醒是在哪个地方。这样能才能深刻了解两个队列的交换关系。

你可能感兴趣的:(Java基础,java)