内容简介:本文聊一下 JUC 下的 LinkedBlockingQueue 队列,先说说 LinkedBlockingQueue 队列的特点,然后再从源码的角度聊一聊 LinkedBlockingQueue 的主要实现~LinkedBlockingQueue 有以下特点:LinkedBlockingQueue 队列继承了
本文聊一下 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 队列的简单源码解析,希望对你的面试或者工作有所帮助,感谢你的阅读~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Python面试经验总结,面试一时爽,一直面试一直爽!
- 算法面试:数组编码面试问题
- 【面试虐菜】—— JAVA面试题(1)
- 如何面试-作为面试官得到的经验
- PHP面试之网络协议面试题
- 如何克服面试紧张心理 ?(面试答题篇Ⅲ)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
程序算法与技巧精选
郭继展 / 2008-5 / 36.00元
《信息科学与技术丛书•程序算法与技巧精选》分17章,139个例题。书中介绍的算法和技巧涉及到随机数函数理论,基础数论,新意幻方,提高程序运行速度和精度,特定数据排序,穷举、递推、递归和迭代等诸多方面。这些算法和技巧大多是作者历年从事教学、软件开发、学术研究和学习的成果总结。 《信息科学与技术丛书•程序算法与技巧精选》内容不涉及计算机专业课程的诸多概念、理论,读者只需要学过C语言,有算法、结构......一起来看看 《程序算法与技巧精选》 这本书的介绍吧!