java源码-ArrayBlockingQueue
开篇
ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列。
- 线程安全是指ArrayBlockingQueue内部通过“互斥锁”保护竞争资源,实现了多线程对竞争资源的互斥访问。
- 有界是指ArrayBlockingQueue对应的数组是有界限的。
- 阻塞队列是指多线程访问竞争资源时,当竞争资源已被某线程获取时,其它要获取该资源的线程需要阻塞等待;而且,ArrayBlockingQueue是按 FIFO(先进先出)原则对元素进行排序,元素都是从尾部插入到队列,从头部开始返回。
ArrayBlockingQueue类图
ArrayBlockingQueue构造函数
ArrayBlockingQueue的构造函数信息表明以下几个信息:
- 线程安全 ReentrantLock lock
- 容量有界 this.items = new Object[capacity];
- 状态同步 Condition notEmpty、Condition notFull
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { final Object[] items; int takeIndex; int putIndex; int count; // 通过lock来保证线程安全,通过lock下的Condition来实现状态同步 final ReentrantLock lock; private final Condition notEmpty; private final Condition notFull; transient Itrs itrs = null; // 构造函数必须指定数组大小,所以是有界的 public ArrayBlockingQueue(int capacity) { this(capacity, false); } public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); } public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) { this(capacity, fair); final ReentrantLock lock = this.lock; lock.lock(); // Lock only for visibility, not mutual exclusion try { int i = 0; try { for (E e : c) { checkNotNull(e); items[i++] = e; } } catch (ArrayIndexOutOfBoundsException ex) { throw new IllegalArgumentException(); } count = i; putIndex = (i == capacity) ? 0 : i; } finally { lock.unlock(); } } }
ArrayBlockingQueue常用操作
ArrayBlockingQueue的add/put/offer方法
ArrayBlockingQueue的所有add()方法其实执行的就是offer()方法,其他核心的逻辑在enqueue方法中。当然所有操作都是执行加锁操作lock.lock()。
- add/offer方法是非阻塞的,如果队列满就直接返回异常
- put()方法是阻塞的,如果队列满就等待,等待notFull的信号量,notFull.await()在take等方法执行的时候会触发notFull.signal()。
- enqueue()方法内部就是往item数组中添加元素、计算元素个数count++、重置putIndex、通知非空信号量notEmpty.signal()。
public boolean add(E e) { return super.add(e); } // 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException。 public boolean offer(E e) { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lock(); try { if (count == items.length) return false; else { enqueue(e); return true; } } 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) { // 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(); }
ArrayBlockingQueue的poll/take/peek方法
ArrayBlockingQueue的所有take相关操作最终都是执行dequeue操作的。
- 所有删除元素操作都是先进行加锁保证线程安全
- poll()和take()方法是阻塞的
- take()和poll()带时间参数是阻塞
- dequeue()内部通过takeIndex参数获取待返回的参数,重置元素个数count,移动下一个元素位置takeIndex等。
public E poll() { final ReentrantLock lock = this.lock; lock.lock(); try { return (count == 0) ? null : dequeue(); } finally { lock.unlock(); } } public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return dequeue(); } finally { lock.unlock(); } } 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(); } } public E peek() { final ReentrantLock lock = this.lock; lock.lock(); try { return itemAt(takeIndex); // null when queue is empty } 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; count--; if (itrs != null) itrs.elementDequeued(); notFull.signal(); return x; }
ArrayBlockingQueue的内部的锁
在ArrayBlockingQueue函数内部的加锁动作,我们发现有lock和lockInterruptibly两种,lock 与 lockInterruptibly比较区别在于:
- lock 优先考虑获取锁,待获取锁成功后,才响应中断。
- lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。
详细区别:
- ReentrantLock.lockInterruptibly允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。
- ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态,然后再中断线程。
ArrayBlockingQueue的内部的信号量
ArrayBlockingQueue由于数组的容量是固定的,所以需要信号量协调put和take动作。
- 在put的时候遇到数组满的时候通过notFull.await()实现等待,直到dequeue()方法消费一个元素后执行notFull.signal()通知可以put新元素。
- 在take的时候遇到数组空的时候通过notEmpty.await()实现等待,直到enqueue()方法新增一个元素后执行notEmpty.signal()通知可以take新元素。
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return dequeue(); } 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(); } ---------------------------------------------------- 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 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; }
ArrayBlockingQueue迭代器
ArrayBlockingQueue迭代器的迭代器比较简单,hasNext()判断下一个元素是否为null,next()通过移动下标获取下一个元素,中间涉及到一些下标到末尾重新从头开始。当然有一些细节代码我直接省略了不影响理解迭代过程。
public Iterator<E> iterator() { return new Itr(); } private class Itr implements Iterator<E> { private int cursor; private E nextItem; private int nextIndex; private E lastItem; private int lastRet; private int prevTakeIndex; private int prevCycles; private static final int NONE = -1; private static final int REMOVED = -2; private static final int DETACHED = -3; Itr() { lastRet = NONE; final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { if (count == 0) { // assert itrs == null; cursor = NONE; nextIndex = NONE; prevTakeIndex = DETACHED; } else { final int takeIndex = ArrayBlockingQueue.this.takeIndex; prevTakeIndex = takeIndex; nextItem = itemAt(nextIndex = takeIndex); cursor = incCursor(takeIndex); if (itrs == null) { itrs = new Itrs(this); } else { itrs.register(this); // in this order itrs.doSomeSweeping(false); } prevCycles = itrs.cycles; } } finally { lock.unlock(); } } private int incCursor(int index) { // assert lock.getHoldCount() == 1; if (++index == items.length) index = 0; if (index == putIndex) index = NONE; return index; } public boolean hasNext() { // assert lock.getHoldCount() == 0; if (nextItem != null) return true; noNext(); return false; } public E next() { // assert lock.getHoldCount() == 0; final E x = nextItem; if (x == null) throw new NoSuchElementException(); final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { if (!isDetached()) incorporateDequeues(); // assert nextIndex != NONE; // assert lastItem == null; lastRet = nextIndex; final int cursor = this.cursor; if (cursor >= 0) { nextItem = itemAt(nextIndex = cursor); // assert nextItem != null; this.cursor = incCursor(cursor); } else { nextIndex = NONE; nextItem = null; } } finally { lock.unlock(); } return x; } }

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
区块链开发公司谈区块链在商业上的应用
对于近期正受科技界和资本市场关注的区块链行业,一句话概括说如果互联网技术解决的是通讯问题的话,区块链技术解决的是信任问题,其在商业领域应用如何呢?我们来从两个方面去进行剖析。 第一方面,区块链技术可以解决基础资产和贸易的真实性,在区块链技术2.0中产生一种智能合约,使供应链上的各方在统一区块链中进行展示,这样交易的各方就不必担心某一方篡改合约或者其他的信息不对称问题导致的利益的损失。 第二方面,区块链技术能解决在供应链贸易上交易的需求,交易中的金融数据可以对供应链中上下游交易真实性给予动态保障,区块链技术可以让每个交易方都变成交易网络中的一个节点,这样任意一方都有一个属于自己的数据库,用来把控交易的整个过程。供应链上各个交易方可以直接在区块链上确认一个交易,形成一个数据记录,通过区块链可以让交易的各方交易过程更加透明,更方便对资金及物流进行监管,避免虚假交易的产生。供应链金融管理应用系统。 传统网络可以实现信息的点到点传递,但无法实现价值的点到点传递。因为信息是允许复制的,而价值必须确权且具有唯—性,因此必须依赖一个中心化机构才能做到价值传递。区块链完美地解决了此问题,提供了一个实现价...
- 下一篇
java8新特性(二)_lambda表达式
最近一直找java8相关新特性的文章,发现都太没有一个连贯性,毕竟大家写博客肯定都有自己的侧重点,这里找到一本书,专门介绍java8新特性的,感觉大家可以看看《写给大忙人看的JavaSE8》.这里我会结合书中的知识以及网上的知识,对于java8 的新特性进行总结,当然我自己写的也会有自己的侧重点。 java8为什么增加了函数式编程 java作为一门面向对象的编程语言诞生于20世纪90年代。在当时,面向对象编程是软件开发的主流模式。 由于最近在并发和事件驱动(或者称为“互动”)编程中的优势,函数式编程又逐渐变得重要起来。 这并不意味着面向对象编程不好,相反,最终的趋势是将面向对象编程和函数编程结合起来。 java8主要在原来面向对象的基础上增加了函数式编程的能力. 函数式编程的语言我就了解过一个_Erlang,有兴趣的大家可以看看Erlang这门语言。比如消息中间件rabbitmq 就是采用erlang写的。只要用过erlang的一定知道坚强2002和阿里的余锋。还有再推荐一个博客为“没有开花的树”的博主。至少对我帮助蛮多的。(有点扯远了) 为什么要使用lambda表达式 lambda表...
相关文章
文章评论
共有0条评论来说两句吧...