Java 核心技术面试精讲
杨晓峰
前 Oracle 首席工程师
125942 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 44 讲
Java 核心技术面试精讲
15
15
1.0x
00:00/00:00
登录|注册

第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?

使用BlockingQueue来实现,由于其提供的等待机制,我们可以少操心很多协调工作。
指定某种结构,比如栈,用它实现一个BlockingQueue,实现思路是怎样的呢?
生产者-消费者场景
SynchronousQueue,这是一个非常奇葩的队列实现,每个删除操作都要等待插入操作,反之每个插入操作也都要等待删除动作。
LinkedBlockingQueue,其行为和内部代码都是基于有界的逻辑实现的,只不过如果我们没有在创建队列时就指定容量,那么其容量限制就自动被设置为Integer.MAX_VALUE,成为了无界队列。
ArrayBlockingQueue是最典型的的有界队列,其内部以final的数组保存数据,数组的大小就决定了队列的边界。
一课一练
队列使用场景与典型用例
线程安全队列一览
队列是非常重要的数据结构,在日常开发中很多线程间数据传递都要依赖于它。
考察对并发包内部不同容器实现的设计目的和实现区别。
弱一致性的另外一个体现是,size等操作准确性是有限的,未必是100%准确。
但是,凡事都是有代价的,Concurrent往往提供了较低的遍历一致性。
Concurrent类型没有类似CopyOnWrite之类容器相对较重的修改开销。
LinkedBlockingQueue内部则是基于锁,并提供了BlockingQueue的等待性方法。
Concurrent类型基于lock-free,在常见的多线程访问场景,一般可以提供较高吞吐量。
知识扩展
考点分析
典型回答
并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
参考文章

该思维导图由 AI 生成,仅供参考

在上一讲中,我分析了 Java 并发包中的部分内容,今天我来介绍一下线程安全队列。Java 标准库提供了非常多的线程安全队列,很容易混淆。
今天我要问你的问题是,并发包中的 ConcurrentLinkedQueue 和 LinkedBlockingQueue 有什么区别?

典型回答

有时候我们把并发包下面的所有容器都习惯叫作并发容器,但是严格来讲,类似 ConcurrentLinkedQueue 这种“Concurrent*”容器,才是真正代表并发。
关于问题中它们的区别:
Concurrent 类型基于 lock-free,在常见的多线程访问场景,一般可以提供较高吞吐量。
而 LinkedBlockingQueue 内部则是基于锁,并提供了 BlockingQueue 的等待性方法。
不知道你有没有注意到,java.util.concurrent 包提供的容器(Queue、List、Set)、Map,从命名上可以大概区分为 Concurrent*、CopyOnWrite和 Blocking等三类,同样是线程安全容器,可以简单认为:
Concurrent 类型没有类似 CopyOnWrite 之类容器相对较重的修改开销。
但是,凡事都是有代价的,Concurrent 往往提供了较低的遍历一致性。你可以这样理解所谓的弱一致性,例如,当利用迭代器遍历时,如果容器发生修改,迭代器仍然可以继续进行遍历。
与弱一致性对应的,就是我介绍过的同步容器常见的行为“fail-fast”,也就是检测到容器在遍历过程中发生了修改,则抛出 ConcurrentModificationException,不再继续遍历。
弱一致性的另外一个体现是,size 等操作准确性是有限的,未必是 100% 准确。
与此同时,读取的性能具有一定的不确定性。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java并发包中的线程安全队列实现有多种类型,其中ConcurrentLinkedQueue和LinkedBlockingQueue是两种常见的实现。它们在实现和性能上有一些区别,ConcurrentLinkedQueue基于无锁实现,适用于多线程访问场景,提供较高的吞吐量;而LinkedBlockingQueue内部基于锁,并提供了BlockingQueue的等待性方法。在java.util.concurrent包中,线程安全容器可以大致分为Concurrent*、CopyOnWrite*和Blocking*三类,每种类型的容器都有其特点和适用场景。了解并发包内部不同容器实现的设计目的和实现区别,以及掌握一些基本的队列本身和数据结构方面知识是非常重要的。文章还介绍了各种线程安全队列的实现特点和使用场景,以及如何根据需求选择合适的队列实现。通过生产者-消费者场景的样例代码,展示了如何使用BlockingQueue来简化协调工作。最后,文章提出了一个思考题,指定某种数据结构,如栈,来实现一个BlockingQueue的思路。整体来说,本文从多个角度深入解析了Java中各种线程安全队列的特点和使用场景,为读者提供了全面的了解和思考。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 核心技术面试精讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(39)

  • 最新
  • 精选
  • 爱新觉罗老流氓
    杨老师,“与弱一致性对应的,就是我介绍过的同步容器常见的行为“fast-fail”,也就是检测到容器在遍历过程中发生了修改,则抛出 ConcurrentModificationException,不再继续遍历。” 这一段落里,快速失败的英文在doc上是“fail-fast”,在ArrayList源码中文档可以搜到。 还有,同步容器不应该是“fail-safe”吗?

    作者回复: 谢谢指出,我查查是不是我记反了

    2018-07-03
    2
    4
  • 灰飞灰猪不会灰飞.烟灭
    老师 线程池中如果线程已经运行结束则删除该线程。如何判断线程已经运行结束了呢?源码中我看见按照线程的状态,我不清楚这些状态值哪来的。java代码有判断线程状态的方法吗?谢谢老师

    作者回复: 所谓结束是指terminated?正常的线程池移除工作线程,要么线程意外退出,比如任务抛异常,要么线程闲置,又规定了闲置时间;线程池中线程是把额外封装的,本来下章写了,内容篇幅超标移到后面了,慢慢来;有,建议学会看文档,自己找答案

    2018-06-21
    3
  • Invocker.C
    求老师解答一个困扰已久的问题,就是初始化arrayblockingqueue的时候,capacity的大小如何评估和设置?望解答

    作者回复: 不清楚你的硬件、业务特点,一个大概原则是尽量让进和出的速率一致,不然出慢,进就block,反过来也不好;实际操作上,你试试找时机检查remaincapacity,就可以判断进出速率的对比

    2018-06-27
    2
    1
  • 杭州
    杨老师你好,遇到个问题,200个并发线程池阻塞读linkBlockingQueue队列,偶尔会出现阻塞时会线程cpu很好。jstack看了很多lock。会不会出现线程离开线程池,去干别的任务,干了一半又回到线程池中干活。两边出现死锁?

    作者回复: 没有太明白问题,线程离开线程池是什么情况?“很好”是很“高”吗?很多“锁”但并没有死锁,我的理解对吗?配合top,看看占cpu的线程栈具体是什么情况吧,

    2018-11-10
  • 夏洛克的救赎
    老师你好,问个题外问题,在jdk10源码 string类中,成员变量coder起到什么作用?如何理解?

    作者回复: 编码,区分拉丁和非拉丁语系

    2018-06-21
  • sunlight001
    这个看着很吃力啊,都没接触过😂
    2018-06-21
    94
  • 丘壑
    栈来实现blockqueue,个人感觉比较好的有 方案一:总共3个栈,其中2个写入栈(A、B),1个消费栈栈C(消费数据),但是有1个写入栈是空闲的栈(B),随时等待写入,当消费栈(C)中数据为空的时候,消费线程(await),触发数据转移,原写入栈(A)停止写入,,由空闲栈(B)接受写入的工作,原写入栈(A)中的数据转移到消费栈(C)中,转移完成后继续(sign)继续消费,2个写入栈,1个消费栈优点是:不会堵塞写入,但是消费会有暂停 方案二:总共4个栈,其中2个写入栈(A、B),2个消费栈(C、D),其中B为空闲的写入栈,D为空闲的消费栈,当消费栈(C)中的数据下降到一定的数量,则触发数据转移,这时候A栈停止写入,由B栈接受写入数据,然后将A栈中的数据转入空闲的消费栈D,当C中的数据消费完了后,则C栈转为空闲,D栈转为激活消费状态,当D栈中的数据消费到一定比例后,重复上面过程,该方案优点即不堵塞写入,也不会造成消费线程暂停
    2018-09-13
    7
    49
  • 吕倩
    老师你好,在读ArrayBlockingQueue源码的时候,发现很多地方都有 final ReentrantLock lock = this.lock; 这样的语句,处于什么原因会将类变量复制一份到局部变量,然后再使用呢?
    2018-12-03
    2
    20
  • Lighters
    希望能够增加一些具体的业务使用场景,否则只是单纯的分析,太抽象了
    2019-04-27
    14
  • 石头狮子
    实现课后题过程中把握以下几个维度, 1,数据操作的锁粒度。 2,计数,遍历方式。 3,数据结构空,满时线程的等待方式,有锁或无锁方式。 4,使用离散还是连续的存储结构。
    2018-06-21
    13
收起评论
显示
设置
留言
39
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部