27. Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。
ArrayBlockingQueue介绍:
ArrayBlockingQueue是一个由数组支持的有界阻塞队列,继承自AbstractBlockingQueue,实现了BlockingQueue接口(Queue接口和Collection接口)。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。
ArrayBlockingQueue常用操作:add,offer,put,peek,pool,take,remove。
add、offer、put都是插入操作。peek,pool,take,remove是取出操作。他们之间的区别和关联:add: 内部是获取的offer方法,将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException。不会阻塞。
offer:将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false。不会阻塞。
put:将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间,只要不被中断,就会插入数据到队列中。会阻塞,可以响应中断。
peek:获取但不移除此队列的头;如果此队列为空,则返回 null。不会阻塞。
pool:与offer对应,获取并移除此队列的头,如果此队列为空,则返回 null。
take:与put对应,获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
remove:与add对应,从此队列中移除指定元素的单个实例(如果存在)。boolean remove(Object o) 返回true/false。remove() 在队列为空时会抛异常NoSuchElementException - if this queue is empty
LinkedBlockingQueue介绍:
一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。可选的容量范围构造方法参数作为防止队列过度扩展的一种方法。如果未指定容量,则它等于 Integer.MAX_VALUE。除非插入节点会使队列超出容量,否则每次插入后会动态地创建链接节点。
-----------------------------以下内容来自http://blog.csdn.net/qq_23359777/article/details/70146778-------------------------------------------------------------------
4.源码分析
下面从源码的角度来看,ArrayBlockingQueue的实现。JDK版本是1.8。
4.1 保存数据的结构
/** The queued items */ final Object[] items;可以看到,是一个Object的数组。
4.2全局锁
/** Main lock guarding all access */ final ReentrantLock lock;注视也说明了,这是一个掌管所有访问操作的锁。全局共享。都会使用这个锁。
4.3 add 和 offer
public boolean add(E e) { return super.add(e); } public boolean offer(E e) { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lock(); // 一直等到获取锁 try { if (count == items.length) //假如当前容纳的元素个数已经等于数组长度,那么返回false return false; else { enqueue(e); // 将元素插入到队列中,返回true return true; } } finally { lock.unlock(); //释放锁 } }把他们放在一起,实际上super.add(e)里面就是调用的offer方法,当offer返回false时,就抛出一个异常,否则返回true。我们直接分析offer方法。
他的实现逻辑是这样子的。
一直等待获取锁 - > 当获取到锁之后,比较当前的元素个数与数组长度,当相等时,那么队列已经满了,无法插入,返回false->否则进行入队操作,返回true。
4.4 put
public void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); //可中断的获取锁 try { while (count == items.length) //当线程从等待中被唤醒时,会比较当前队列是否已经满了 notFull.await(); //notFull = lock.newCondition 表示队列不满这种状况,假如现场在插入的时候 enqueue(e); //当前队列已经满了时,则需要等到这种情况的发生。 } finally { //可以看出这是一种阻塞式的插入方式 lock.unlock(); } }
4.5 poll
如前文所说,poll方法与offer相互对应,见源码
public E poll() { final ReentrantLock lock = this.