阻塞队列—LinkedBlockingQueue源码分析( 四 )

poll方法去除了take方法中元素为空后阻塞等待这一步骤 , 这里也就不详细说了 。 同理 , poll(long timeout, TimeUnit unit)也和offer(E e, long timeout, TimeUnit unit)一样 , 利用了Condition的awaitNanos方法来进行阻塞等待直至超时 。 这里就不列出来说了 。
获取元素方法public E peek() {if (count.get() == 0)return null;final ReentrantLock takeLock = this.takeLock;takeLock.lock();try {Node first = head.next;if (first == null)return null;elsereturn first.item;} finally {takeLock.unlock();}}加锁后 , 获取到head节点的next节点 , 如果为空返回null , 如果不为空 , 返回next节点的item值 。
删除元素方法public boolean remove(Object o) {if (o == null) return false;// 两个lock全部上锁fullyLock();try {// 从head开始遍历元素 , 直到最后一个元素for (Node trail = head, p = trail.next;p != null;trail = p, p = p.next) {// 如果找到相等的元素 , 调用unlink方法删除元素if (o.equals(p.item)) {unlink(p, trail);return true;}}return false;} finally {// 两个lock全部解锁fullyUnlock();}}void fullyLock() {putLock.lock();takeLock.lock();}void fullyUnlock() {takeLock.unlock();putLock.unlock();}因为remove方法使用两个锁全部上锁 , 所以其他操作都需要等待它完成 , 而该方法需要从head节点遍历到尾节点 , 所以时间复杂度为O(n) 。 我们来看看unlink方法 。
void unlink(Node p, Node trail) {// p的元素置为nullp.item = null;// p的前一个节点的next指向p的next , 也就是把p从链表中去除了trail.next = p.next;// 如果last指向p , 删除p后让last指向trailif (last == p)last = trail;// 如果删除之前元素是满的 , 删除之后就有空间了 , 唤醒生产线程放入元素if (count.getAndDecrement() == capacity)notFull.signal();}总结LinkedBlockingQueue是一个阻塞队列 , 内部由两个ReentrantLock来实现出入队列的线程安全 , 由各自的Condition对象的await和signal来实现等待和唤醒功能 。 它和ArrayBlockingQueue的不同点在于:

  • 队列大小有所不同 , ArrayBlockingQueue是有界的初始化必须指定大小 , 而LinkedBlockingQueue可以是有界的也可以是无界的(Integer.MAX_VALUE) , 对于后者而言 , 当添加速度大于移除速度时 , 在无界的情况下 , 可能会造成内存溢出等问题 。
  • 数据存储容器不同 , ArrayBlockingQueue采用的是数组作为数据存储容器 , 而LinkedBlockingQueue采用的则是以Node节点作为连接对象的链表 。
  • 由于ArrayBlockingQueue采用的是数组的存储容器 , 因此在插入或删除元素时不会产生或销毁任何额外的对象实例 , 而LinkedBlockingQueue则会生成一个额外的Node对象 。 这可能在长时间内需要高效并发地处理大批量数据的时 , 对于GC可能存在较大影响 。
  • 两者的实现队列添加或移除的锁不一样 , ArrayBlockingQueue实现的队列中的锁是没有分离的 , 即添加操作和移除操作采用的同一个ReenterLock锁 , 而LinkedBlockingQueue实现的队列中的锁是分离的 , 其添加采用的是putLock , 移除采用的则是takeLock , 这样能大大提高队列的吞吐量 , 也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据 , 以此来提高整个队列的并发性能 。
PS:以上代码提交在 Github :
【阻塞队列—LinkedBlockingQueue源码分析】文章持续更新 , 可以公众号搜一搜「 一角钱技术 」第一时间阅读 ,本文 GitHub org_hejianhui/JavaStudy 已经收录 , 欢迎 Star 。