1. 简单的开篇

LinkedBlockingQueueConcurrentLinkedQueue 是 Java 高并发场景中最常使用的队列。尽管这两个队列经常被用作并发场景的数据结构,但它们之间仍有细微的特征和行为差异。

在这篇文章中,我将和大家一起探讨这两者之间的异同点。欢迎大家在留言讨论~

2. LinkedBlockingQueue

首先 LinkedBlockingQueue 是一个 “可选且有界” 的阻塞队列实现,你可以根据需要指定队列的大小。

接下来,我将创建一个LinkedBlockingQueue,它最多可以包含100个元素:

BlockingQueue<Integer> boundedQueue = new LinkedBlockingQueue<>(100);

当然,我们也可以通过不指定大小,来创建一个无界的 LinkedBlockingQueue

BlockingQueue<Integer> unboundedQueue = new LinkedBlockingQueue<>();

无界队列表示在创建时未指定队列的大小。因此,队列可以随着元素的添加而动态增长。但是,如果没有剩余内存,则队列将抛出 java.lang.OutOfMemory 错误。

这里留下一个问题给大家思考: 创建无界队列是好还是坏呢?

我们还可以从现有的集合来创建 LinkedBlockingQueue

Collection<Integer> listOfNumbers = Arrays.asList(1,2,3,4,5);
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(listOfNumbers);

LinkedBlockingQueue 实现了BlockingQueue接口,该接口为它提供了阻塞性质

阻塞队列表示如果访问线程已满(当队列有界时)或变为空,则队列将阻塞该线程。如果队列已满,则添加新元素将阻塞访问线程,除非新元素有可用空间。类似地,如果队列为空,则访问元素会阻塞调用线程:

ExecutorService executorService = Executors.newFixedThreadPool(1);
LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
executorService.submit(() -> {
  try {
    queue.take();
  }
  catch (InterruptedException e) {
    // exception handling
  }
});

在上面的代码片段中,我们正在访问一个空队列。因此,take() 方法阻塞调用线程。

LinkedBlockingQueue 的阻塞特性与一些开销相关。这个代价是因为每个puttake操作在生产者线程或使用者线程之间都是锁争用的。因此,在许多生产者和消费者的情况下,put和take 动作可能会慢一些。

3. ConcurrentLinkedQueue

首先声明,ConcurrentLinkedQueue 是一个无边界、线程安全且无阻塞的队列

创建一个空的 ConcurrentLinkedQueue:

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

同上面一样,我们也可以从现有集合创建 ConcurrentLinkedQueue

Collection<Integer> listOfNumbers = Arrays.asList(1,2,3,4,5);
ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>(listOfNumbers);

不同于 LinkedBlockingQueueConcurrentLinkedQueue是非阻塞的队列。因此,即使队列为空(empty),它也不会阻塞线程。相反,它会返回 (null) 。虽然它是无界的,但如果没有额外的内存来添加新元素,它依旧会抛出 java.lang.OutOfMemory 错误。

除了非阻塞之外,ConcurrentLinkedQueue还有其他特性。

在任何生产者-消费者场景中,消费者都不会满足于生产者;但是,多个生产者将相互竞争:

int element = 1;
ExecutorService executorService = Executors.newFixedThreadPool(2);
ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();
 
Runnable offerTask = () -> queue.offer(element);
 
Callable<Integer> pollTask = () -> {
  while (queue.peek() != null) {
    return queue.poll().intValue();
  }
  return null;
};
 
executorService.submit(offerTask);
Future<Integer> returnedElement = executorService.submit(pollTask);
assertThat(returnedElement.get().intValue(), is(equalTo(element)));

第一个任务 offerTask 向队列中添加元素,第二个任务 pollTask 从队列中检索元素。pollTask 首先检查队列中的元素,因为ConcurrentLinkedQueue是非阻塞的,并且可以返回null

4. 求同

LinkedBlockingQueueConcurrentLinkedQueue 都是队列实现,并具有一些共同特征。让我们讨论一下这两个队列的相似之处:

  1. 实现 Queue 接口
  2. 它们都使用 linked nodes 存储节点
  3. 都适用于并发访问场景

5. 存异

尽管这两种队列都有某些相似之处,但也有一些实质性的特征差异:

特性 LinkedBlockingQueue ConcurrentLinkedQueue
阻塞性 阻塞队列,并实现blocking queue接口 非阻塞队列,不实现blocking queue接口
队列大小 可选的有界队列,这意味着可以在创建期间定义队列大小 无边界队列,并且没有在创建期间指定队列大小的规定
锁特性 基于锁的队列 无锁队列
算法 锁的实现基于 “双锁队列(two lock queue)” 算法 依赖于Michael&Scott算法来实现无阻塞、无锁队列
实现 双锁队列 算法机制中,LinkedBlockingQueue使用两种不同的锁,putLocktakeLockput/take操作使用第一个锁类型,take/poll操作使用另一个锁类型 使用CAS(Compare And Swap)进行操作
阻塞行为 当队列为空时,它会阻塞访问线程 当队列为空时返回 null,它不会阻塞访问线程

6. 总结才能进步

首先,我们分别讨论了这两种队列实现及其一些特性、相似性、以及它们之间的差异。这样的比较,是否让你对这两种队列有了更深刻的印象?

公众号: 锅外的大佬 ,欢迎加群~

博客地址: http://www.developlee.top

LinkedBlockingQueue 和 ConcurrentLinkedQueue的区别的更多相关文章

  1. java LinkedBlockingQueue和ConcurrentLinkedQueue的区别

    实现上看,两者都继承于AbstractQueue,但是ConcurrentLinkedQueue实现了Queue,而LinkedBlockingQueue实现了BlockingQueue,Blocki ...

  2. (原创)JAVA阻塞队列LinkedBlockingQueue 以及非阻塞队列ConcurrentLinkedQueue 的区别

    阻塞队列:线程安全 按 FIFO(先进先出)排序元素.队列的头部 是在队列中时间最长的元素.队列的尾部 是在队列中时间最短的元素.新元素插入到队列的尾部,并且队列检索操作会获得位于队列头部的元素.链接 ...

  3. LinkedBlockingQueue和ConcurrentLinkedQueue详细用法

    1.LinkedBlockingQueue<E>:java.util.concurrent API中的解释: public class LinkedBlockingQueue<E&g ...

  4. LinkedBlockingQueue 与ConcurrentLinkedQueue队列的不同与同

    LinkedBlockingQueue 的API中,从队列中获取元素,有以下几个方法: 1.take():原文:Retrieves and removes the head of this queue ...

  5. Java并发编程原理与实战三十五:并发容器ConcurrentLinkedQueue原理与使用

    一.简介 一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素进行排序.队列的头部 是队列中时间最长的元素.队列的尾部 是队列中时间最短的元素.新的元素插入到队列的尾部,队列 ...

  6. Java同步数据结构之ConcurrentLinkedQueue

    前言 前面介绍的Queue都是通过Lock锁实现的阻塞队列,今天介绍一种非阻塞队列ConcurrentLinkedQueue,所谓非阻塞,其实就是通过CAS代替加锁来实现的高效的非阻塞队列.当许多线程 ...

  7. JUC源码分析-集合篇(六)LinkedBlockingQueue

    JUC源码分析-集合篇(六)LinkedBlockingQueue 1. 数据结构 LinkedBlockingQueue 和 ConcurrentLinkedQueue 一样都是由 head 节点和 ...

  8. java.util.concurrent包下集合类的特点与适用场景

    java.util.concurrent包,此包下的集合都不允许添加null元素 序号 接口 类 特性 适用场景 1 Queue.Collection ArrayBlockingQueue 有界.阻塞 ...

  9. Java多线程(六) —— 线程并发库之并发容器

    参考文献: http://www.blogjava.net/xylz/archive/2010/07/19/326527.html 一.ConcurrentMap API 从这一节开始正式进入并发容器 ...

随机推荐

  1. 疑难杂症1-去掉网站里的特殊编码&#65279

    编辑器:Visual Studio 2019项目框架:Net Core 3.1 + AutoFac 特殊符号&#65279,这是编码UTF-8 + BOM的特殊标记,是隐藏的,但是会被浏览器翻 ...

  2. SpringBoot-使用lombok插件运行报错

    SpringBoot-使用lombok插件运行报错 标签(空格分隔): java,SpringBoot 1.报错信息 2.解决方案 1.IDEA编辑器安装lombok插件 2.编译注解配置-Enabl ...

  3. gc 模块常用函数

    """ 1.gc.set_debug(flags) 设置gc的debug日志,一般设置为gc.DEBUG_LEAK 2.gc.collect([generation]) ...

  4. 4.13 省选模拟赛 传销组织 bitset 强连通分量 分块

    考试的时候昏了头 没算空间 这道题我爆零了.值得注意的是 一般认为bitset的空间是 int 的1/w倍 对于那m条边 无论如何构造 这m条关系都是存在的 题目其实是想让我们用这m条关系来计算给出的 ...

  5. MapReduce之GroupingComparator分组(辅助排序、二次排序)

    指对Reduce阶段的数据根据某一个或几个字段进行分组. 案例 需求 有如下订单数据 现在需要找出每一个订单中最贵的商品,如图 需求分析 利用"订单id和成交金额"作为key,可以 ...

  6. ElasticSearch添加索引

    1. 编写索引内容 节点解释: settings:配置信息 "number_of_replicas": 0  不需要备份(单节点的ElasticSearch使用) "ma ...

  7. 【SCOI2005】互不侵犯 题解(状压DP)

    前言:一道状压DP的入门题(可惜我是个DP蒟蒻QAQ) ------------------ 题意简述:求在一个$n*n$的棋盘中放$k$个国王的方案数.注:当在一个格子中放入国王后,以此格为中心的九 ...

  8. PHP 之 Composer 新手入门指南

    自2012年3月1日发布以来,Composer因提供了PHP迫切需要的东西:依赖项管理而广受欢迎.实际上,Composer是将所有第三方软件(例如CSS框架,jQuery插件等)引入你的项目的一种方法 ...

  9. 用Python来搞副业?这届大学生到底有多野……

    最近,我在知乎上偶然发现一个有意思的问题: 「大学生实习被当作廉价劳动力,你怎么看?」 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很 ...

  10. FreeAnchor 国科大