本文聊一下 JUC 下的 LinkedBlockingQueue 队列,先说说 LinkedBlockingQueue 队列的特点,然后再从源码的角度聊一聊 LinkedBlockingQueue 的主要实现~

LinkedBlockingQueue 有以下特点:

  • LinkedBlockingQueue 是阻塞队列,底层是单链表实现的~
  • 元素从队列尾进队,从队列头出队,符合FIFO~
  • 可以使用 Collection 和 Iterator 两个接口的所有操作,因为实现了两者的接口~
  • LinkedBlockingQueue 队列读写操作都加了锁,但是读写用的是两把不同的锁,所以可以同时读写操作~

LinkedBlockingQueue 队列继承了 AbstractQueue 类,实现了 BlockingQueue 接口,LinkedBlockingQueue 主要有以下接口:

//将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量)
//在成功时返回 true,如果此队列已满,则抛IllegalStateException。
boolean add(E e); //将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量)
// 将指定的元素插入此队列的尾部,如果该队列已满,
//则在到达指定的等待时间之前等待可用的空间,该方法可中断
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; //将指定的元素插入此队列的尾部,如果该队列已满,则一直等到(阻塞)。
void put(E e) throws InterruptedException; //获取并移除此队列的头部,如果没有元素则等待(阻塞),
//直到有元素将唤醒等待线程执行该操作
E take() throws InterruptedException; //获取并移除此队列的头,如果此队列为空,则返回 null。
E poll();
//获取并移除此队列的头部,在指定的等待时间前一直等到获取元素, //超过时间方法将结束
E poll(long timeout, TimeUnit unit) throws InterruptedException; //从此队列中移除指定元素的单个实例(如果存在)。
boolean remove(Object o); //获取但不移除此队列的头元素,没有则跑异常NoSuchElementException
E element(); //获取但不移除此队列的头;如果此队列为空,则返回 null。
E peek();

LinkedBlockingQueue 队列的读写方法非常的多,但是常用的是 put()take()方法,因为它们两是阻塞的,所以我们就从源码的角度来聊一聊 LinkedBlockingQueue 队列中这两个方法的实现。

先来看看 put()方法,源码如下:

public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// 预先设置 c 的值为 -1,表示失败
int c = -1;
Node<E> node = new Node<E>(e);
// 获取写锁
final ReentrantLock putLock = this.putLock;
// 获取当前队列的大小
final AtomicInteger count = this.count;
// 设置可中断锁
putLock.lockInterruptibly();
try {
// 队列满了
// 当前线程阻塞,等待其他线程的唤醒(其他线程 take 成功后就会唤醒此处线程)
while (count.get() == capacity) {
// 无限期等待
notFull.await();
}
// 新增到队列尾部
enqueue(node);
// 获取当前的队列数
c = count.getAndIncrement();
// 如果队列未满,尝试唤醒一个put的等待线程
if (c + 1 < capacity)
notFull.signal();
} finally {
// 释放锁
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}

put()方法的源码并不难,非常容易就看懂,put()方法的过程大概如下:

  • 1、先加锁,保证容器的并发安全~
  • 2、队列新增数据,将数据追加到队列尾部~
  • 3、新增时,如果队列满了,当前线程是会被阻塞的,等待被唤醒~
  • 4、新增数据成功后,在适当时机,会唤起 put 的等待线程(队列不满时),或者 take 的等待线程(队列不为空时),这样保证队列一旦满足 put 或者 take 条件时,立马就能唤起阻塞线程,继续运行,保证了唤起的时机不被浪费offer 就有两两种,一种是直接返回 false,另一种是超过一定时间后返回 false~
  • 5、释放锁~

其他的新增方法,例如 offer,可以查看源码,跟put() 方法大同小异,相差不大~

再来看看 take()方法,源码如下:

public E take() throws InterruptedException {
E x;
// 默认负数
int c = -1;
// 当前链表的个数
final AtomicInteger count = this.count;
//获取读锁
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 当队列为空时,阻塞,等待其他线程唤醒
while (count.get() == 0) {
notEmpty.await();
}
// 从队列的头部拿出一个元素
x = dequeue();
//减一操作,C比真实的队列数据大一
c = count.getAndDecrement();
// c 大于 0 ,表示队列有值,可以唤醒之前被阻塞的读线程
if (c > 1)
notEmpty.signal();
} finally {
// 释放锁
takeLock.unlock();
}
// 队列未满,可以唤醒 put 等待线程~
if (c == capacity)
signalNotFull();
return x;
}

take()方法跟 put() 方法类似,是一个相反的操作,我就不做过多的说明了~

以上就是 LinkedBlockingQueue 队列的简单源码解析,希望对你的面试或者工作有所帮助,感谢你的阅读~

欢迎关注公众号【互联网平头哥】,一起成长,一起进步~。

Java 经典面试题:聊一聊 JUC 下的 LinkedBlockingQueue的更多相关文章

  1. 2019年19道java经典面试题(附答案)

    1.不可变对象 指对象一旦被创建状态不能再改变.任何修改都会创建一个新的对象,如 String.Integer及其它包装类. 2.能否创建一个包含可变对象的不可变对象? 可以.不要共享可变对象的引用就 ...

  2. 115道Java经典面试题(面中率最高、最全)

    115道Java经典面试题(面中率最高.最全) Java是一个支持并发.基于类和面向对象的计算机编程语言.下面列出了面向对象软件开发的优点: 代码开发模块化,更易维护和修改. 代码复用. 增强代码的可 ...

  3. Java 经典面试题:聊一聊 JUC 下的 CopyOnWriteArrayList

    ArrayList 是我们常用的工具类之一,但是在多线程的情况下,ArrayList 作为共享变量时,并不是线程安全的.主要有以下两个原因: 1. ArrayList 自身的 elementData. ...

  4. LeetCode随缘刷题之Java经典面试题将一个字符串数组进行分组输出,每组中的字符串都由相同的字符组成

    今天给大家分享一个Java经典的面试题,题目是这样的: 本题是LeetCode题库中的49题. 将一个字符串数组进行分组输出,每组中的字符串都由相同的字符组成 举个例子:输入["eat&qu ...

  5. Java经典面试题+答案(全)

    这套面试题主要目的是帮助那些还没有java软件开发实际工作经验,而正在努力寻找java软件开发工作的朋友在笔试时更好地赢得笔试和面试. 1.一个".java"源文件中是否可以包括多 ...

  6. BATJ等公司必问的8道Java经典面试题,你都会了吗?

    1.谈谈你对 Java 平台的理解?“Java 是解释执行”,这句话正确吗? 考点分析: 对于这类笼统的问题,你需要尽量表现出自己的思维深入并系统化,Java 知识理解得也比较全面,一定要避免让面试官 ...

  7. BAT等公司必问的8道Java经典面试题,你都会了吗?

    工作多年以及在面试中,我经常能体会到,有些面试者确实是认真努力工作,但坦白说表现出的能力水平却不足以通过面试,通常是两方面原因: 1.“知其然不知其所以然”.做了多年技术,开发了很多业务应用,但似乎并 ...

  8. Java经典面试题-不古出品

    @ 目录 一.Java 基础 1.JDK 和 JRE 有什么区别? 2.== 和 equals 的区别是什么? 3.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗? ...

  9. Java经典面试题

    1. Java中的异常处理机制的简单原理和应用. 当Java 程序违反了Java的语义规则时,Java虚拟机就会将发生的错误表示为一个异常.违反语义规则包括2种情况.一种是Java类库内置的语义检查. ...

随机推荐

  1. 宏定义#define和内联函数inline的区别

    1 宏定义在预编译的时候进行字符串替换.内联函数在编译的时候进行函数展开. 2 宏定义没有类型检查.内联函数会进行参数列表.返回值等类型检查.

  2. 数据结构和算法(Golang实现)(10)基础知识-算法复杂度主方法

    算法复杂度主方法 有时候,我们要评估一个算法的复杂度,但是算法被分散为几个递归的子问题,这样评估起来很难,有一个数学公式可以很快地评估出来. 一.复杂度主方法 主方法,也可以叫主定理.对于那些用分治法 ...

  3. AJ学IOS 之微博项目实战(2)微博主框架-自定义导航控制器NavigationController

    AJ分享,必须精品 一:添加导航控制器 上一篇博客完成了对底部的TabBar的设置,这一章我们完成自定义导航控制器(NYNavigationController). 为啥要做自定义呢,因为为了更好地封 ...

  4. 植物大战僵尸的代码如何使用python来实现

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:程序IT圈 PS:如有需要Python学习资料的小伙伴可以加点击下方链 ...

  5. Daily Scrum 1/12/2016

    Zhaoyang & Yandong: Optimize the speech input interface Fuchen: Code refactor in the NLP module ...

  6. F - What Is Your Grade?

    “Point, point, life of student!” This is a ballad(歌谣)well known in colleges, and you must care about ...

  7. C++学习--编译优化

    常量折叠 把常量表达式的值求出来作为常量嵌在最终生成的代码中. 疑问:对于一个很复杂的常量表达式,编译器会算出结果再编译吗?亦或者是把这个表达式完全翻译成机器码,最终留给程序去解决? 分情况: 涉及的 ...

  8. Apache solr velocity模块 漏洞复现

    0x01 Solr简单介绍 Solr是建立在Apache Lucene ™之上的一个流行.快速.开放源代码的企业搜索平台. Solr具有高度的可靠性,可伸缩性和容错能力,可提供分布式索引,复制和负载平 ...

  9. jmeter5.1.1 生成html报告

    1.首先需要准备好 .jmx 脚本 2.修改jmeter.properties文件(把注解去掉,报告中才能展示所需信息) jmeter.save.saveservice.output_format=x ...

  10. SQL Server 之T-SQL基本语句 (1)

    花了一天的时间看完了一本<SQL必知必会>,举个范例,来总结一下零碎的知识点.一般关于数据库操作的项目都会涉及到数据库的基本查询语句.在这里面就主要讲解一些基本常用的sql使用方法. 注: ...