首页 > 学院 > 开发设计 > 正文

JDK 1.8 ArrayBlockingQueue 源码阅读(二)获取

2019-11-06 07:09:54
字体:
来源:转载
供稿:网友

在上一节,介绍了ArrayBlockingQueue的添加元素的方法,本节,结合源码给大家介绍一下获取元素的方法。

获取元素的方法有下述几种

E poll() 立刻返回,如果队列为空,返回null

E take() 如果队列不为空,返回队首元素,否则阻塞到队列不为空

E poll(long timeout, TimeUnit unit) 等待timeout 时间的poll

E peek() 获得队首的元素,并不将这个元素弹出

分别看一下这几个方法的源码

1.E poll()

    public E poll() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return (count == 0) ? null : dequeue();        } finally {            lock.unlock();        }    }

此方法先获得lock,如果lock获得成功,会先去判断count 的值,如果这个值为0,那就说明此队列为空,立刻返回null

2.E take()

 public E take() throws InterruptedException {        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == 0)                notEmpty.await();            return dequeue();        } finally {            lock.unlock();        }    }take 方法和poll方法的区别就在于,如果count为0,那么在notEmpty上等待,直到被唤醒,这里有个需要遵循的原则,await/wait方法调用的时候,一定要确保本线程持有依赖的状态(这里边为count)的锁,并且被唤醒之后,依旧要去检查依赖条件的状态,这是《java 并发编程实战》推荐的使用方式,原因此处不再多说。

3.E poll(long timeout, TimeUnit unit)

我们看到此方法传递了一个超时时间,也就是在条件队列上等待相应的时间,如果等待超过这个时间,那就返回空。

public E poll(long timeout, TimeUnit unit) throws InterruptedException {        long nanos = unit.toNanos(timeout);        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == 0) {                if (nanos <= 0)                    return null;                nanos = notEmpty.awaitNanos(nanos);            }            return dequeue();        } finally {            lock.unlock();        }    }

4.E peek()  只是获得元素,并不将元素从队列中删除,这个方法很简单,只是将相应位置的元素返回,不对队列进行任何操作
    public E peek() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return itemAt(takeIndex); // null when queue is empty        } finally {            lock.unlock();        }    }

5. 再来分析一下出队的方法,这个方法是几个获取方法的核心

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;        count--;        if (itrs != null)            itrs.elementDequeued();        notFull.signal();        return x;    }看这个方法,感觉不难,只是将takeIndex的内容取出来,并将该位置的元素设置为空,然后检查一下队列是不是空了,然后唤醒在notFull条件等待的线程,最后操作一下迭代器

6.remove ,

 /**     * Removes a single instance of the specified element from this queue,     * if it is present.  More formally, removes an element {@code e} such     * that {@code o.equals(e)}, if this queue contains one or more such     * elements.     * Returns {@code true} if this queue contained the specified element     * (or equivalently, if this queue changed as a result of the call).     *     * <p>Removal of interior elements in circular array based queues     * is an intrinsically slow and disruptive Operation, so should     * be undertaken only in exceptional circumstances, ideally     * only when the queue is known not to be accessible by other     * threads.     *     * @param o element to be removed from this queue, if present     * @return {@code true} if this queue changed as a result of the call     */    public boolean remove(Object o) {        if (o == null) return false;        final Object[] items = this.items;        final ReentrantLock lock = this.lock;        lock.lock();        try {            if (count > 0) {                final int putIndex = this.putIndex;                int i = takeIndex;                do {                    if (o.equals(items[i])) {                        removeAt(i);                        return true;                    }                    if (++i == items.length)                        i = 0;                } while (i != putIndex);            }            return false;        } finally {            lock.unlock();        }    }这个remove方法,首先判断对列是否有元素,然后从takeIndex 开始调用equals 方法,判断是否是相同的元素,如果相同,会去调removeAt(i);

我们再看removeAt这个方法,ArrayBlockingQueue支持删除任意位置上的元素

void removeAt(final int removeIndex) {        // assert lock.getHoldCount() == 1;        // assert items[removeIndex] != null;        // assert removeIndex >= 0 && removeIndex < items.length;        final Object[] items = this.items;        if (removeIndex == takeIndex) {            // removing front item; just advance            items[takeIndex] = null;            if (++takeIndex == items.length)                takeIndex = 0;            count--;            if (itrs != null)                itrs.elementDequeued();        } else {            // an "interior" remove            // slide over all others up through putIndex.            final int putIndex = this.putIndex;            for (int i = removeIndex;;) {                int next = i + 1;                if (next == items.length)                    next = 0;                if (next != putIndex) {                    items[i] = items[next];                    i = next;                } else {                    items[i] = null;                    this.putIndex = i;                    break;                }            }            count--;            if (itrs != null)                itrs.removedAt(removeIndex);        }        notFull.signal();    }这个方法,首先判断了需要删除的元素是否是takeIndex所指向的元素,如果是,只需要将这个元素设置为null,并修改一下takeIndex 就好,但是如果不是,就需要将后面的元素全都往前移动一个位置,并且,迭代器也将固定位置的元素删除。

看到现在,貌似没有发现迭代器有什么明显的作用,暂且先认为他是这个ArrayBlockingQueue的一个链表实现的副本,等下节我们再研究


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表