LinkedBlockingQueue 学习
LinkedBlockingQueue 链表队列,其元素构成为:
static class Node<E> {
E item;
Node<E> next;
Node(E x) { item = x; }
}
该队列有两种锁及判断队列不为空和队列未满的条件对象
/** 用于消费队列的锁,如操作:take,poll 等等 */
private final ReentrantLock takeLock = new ReentrantLock(); /** 用于消费时判断不为空的条件对象 */
private final Condition notEmpty = takeLock.newCondition(); /** 添加元素的锁,如操作:put,offer等等 */
private final ReentrantLock putLock = new ReentrantLock(); /** 用于添加元素时判断队列未满的条件对象 */
private final Condition notFull = putLock.newCondition();
队列操作
添加元素
有三种方式:
1.offer
当队列已满时,添加失败,返回false,源码如下:
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
//判断队列是否已满,若队列已满,则返回false
if (count.get() == capacity)
return false;
int c = -1;
Node<E> node = new Node<E>(e);
//获取放入元素的锁
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
//队列未满,这里是double检查,是获取锁后又检查了一次队列是否已满
if (count.get() < capacity) {
//添加元素到队尾
enqueue(node);
//获取添加元素前的队列大小,并将队列大小+1
c = count.getAndIncrement();
//判断队列未满
if (c + 1 < capacity)
//则通知下一个线程继续添加元素
notFull.signal();
}
} finally {
putLock.unlock();
}
//队列中有元素,则通知消费线程可以消费
if (c == 0)
signalNotEmpty();
return c >= 0;
}
2.put
添加元素到队尾,若队列已满,则添加操作进入到阻塞状态,直到队列中有元素有出队。
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
//这里是double检查,是获取锁后又检查了一次队列是否已满,如果队列已满,则线程进入阻塞,直到队列中有元素出队
while (count.get() == capacity) {
notFull.await();
}
//添加元素到队尾
enqueue(node);
c = count.getAndIncrement();
//判断队列未满
if (c + 1 < capacity)
//通知其它线程添加元素
notFull.signal();
} finally {
putLock.unlock();
}
//判断队列已有元素,则通知阻塞的消费线程进行消费
if (c == 0)
signalNotEmpty();
}
put指定超时的操作
指定put的等待超时时间,等待超时后,则返回操作false
//指定超时时间
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException { if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
//当超时时间过了后,则不再继续待,返回操作false
if (nanos <= 0)
return false;
//自旋锁,计算超时时间
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
3.add
实际上执行的是offer操作,判断offer操作是否成功,若失败,则抛出队列已满的异常信息,其实现在父类AbstractQueue中
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
出队操作
1.take
与put相反,若队列为空,则阻塞等待,直到队列有元素入队
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 = count.getAndDecrement();
//判断队列中有元素,通知下一个线程进行消费
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
//判断队列未满,通知入队的线程进行入队操作
if (c == capacity)
signalNotFull();
return x;
}
take指定超时时间
当指定了take操作的超时时间后,take等待超时时,若队列还未有元素,则返回null
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
//当队列为空时,进入到阻塞
while (count.get() == 0) {
//超时时间已过,则返回null,不再阻塞等待
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
2.poll
若队列为空,则直接返回null,否则返回队头元素
public E poll() {
final AtomicInteger count = this.count;
//队列为空,则返回null
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
//获取消费锁
takeLock.lock();
try {
//double检查,再次判断队列不为空,则返回队头元素
if (count.get() > 0) {
x = dequeue();
c = count.getAndDecrement();
//队列还有元素,则通知其它消费线程进行消费操作
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
//判断出队后,队列未满,则通知入队线程进行入队操作
if (c == capacity)
signalNotFull();
return x;
}
3.remove
poll操作,若返回null,则返回空队列异常,其操作在父类AbstractQueue中
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
4.peek
返回队列第一个元素,元素并不出队。
public E peek() {
//队列为空,则返回null
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
//只是返回队头元素的引用,元素不出队
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
其它操作
1.remove(object)
移除指定元素
public boolean remove(Object o) {
if (o == null) return false;
//入队锁和消费锁,均上锁
fullyLock();
try {
//从队头开始,遍历队列
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
//找到指定的元素
if (o.equals(p.item)) {
//移除该元素,将该元素的前一个和后一个关连起来
unlink(p, trail);
//若队列中有指定元素,返回true
return true;
}
}
//未找到指定元素,返回false
return false;
} finally {
fullyUnlock();
}
}
2.contains(object)
判断队列是否存在指定元素
public boolean contains(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> p = head.next; p != null; p = p.next)
if (o.equals(p.item))
return true;
return false;
} finally {
fullyUnlock();
}
}
3.toArray() ,toArray(T[] a)
将队列转换成数组
public Object[] toArray() {
fullyLock();
try {
int size = count.get();
Object[] a = new Object[size];
int k = 0;
for (Node<E> p = head.next; p != null; p = p.next)
a[k++] = p.item;
return a;
} finally {
fullyUnlock();
}
}
public <T> T[] toArray(T[] a) {
fullyLock();
try {
int size = count.get();
if (a.length < size)
a = (T[])java.lang.reflect.Array.newInstance
(a.getClass().getComponentType(), size);
int k = 0;
for (Node<E> p = head.next; p != null; p = p.next)
a[k++] = (T)p.item;
if (a.length > k)
a[k] = null;
return a;
} finally {
fullyUnlock();
}
}
4.clear()
清空队列
public void clear() {
fullyLock();
try {
for (Node<E> p, h = head; (p = h.next) != null; h = p) {
h.next = h;
p.item = null;
}
head = last;
// assert head.item == null && head.next == null;
if (count.getAndSet(0) == capacity)
notFull.signal();
} finally {
fullyUnlock();
}
}
入队实现
private void enqueue(Node<E> node) {
//将原队尾元素指向新元素node,新元素的node的next为null
last = last.next = node;
}
出队实现
private E dequeue() {
//取队头元素
Node<E> h = head;
//取出队头元素的下一个元素
Node<E> first = h.next;
//将原队头元素的下一个元素设置指向为原队头,即本身node,即该node只有自已引用自己
h.next = h; // help GC
//设置新队头元素
head = first;
//取出节点数据
E x = first.item;
//将节点的数据设置为null,则该node除了自己引用自己外,无其它引用,可以被垃圾回收
first.item = null;
return x;
}
LinkedBlockingQueue 学习的更多相关文章
- Java并发包源码学习系列:阻塞队列实现之LinkedBlockingQueue源码解析
目录 LinkedBlockingQueue概述 类图结构及重要字段 构造器 出队和入队操作 入队enqueue 出队dequeue 阻塞式操作 E take() 阻塞式获取 void put(E e ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- JAVA并发编程J.U.C学习总结
前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...
- LinkedBlockingQueue的put,add跟offer的区别
LinkedBlockingQueue的put,add和offer的区别 最近在学习<<Java并发编程实践>>,有很多java.util.concurrent包下的新类.Li ...
- 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法
在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出).Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQ ...
- Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍
1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...
- Android(java)学习笔记267:Android线程池形态
1. 线程池简介 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力. 假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...
- 从使用到原理学习Java线程池
线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源.在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收. 所 ...
- 01-Java学习笔记
本系列笔记由常彦博整理,请知悉 目 录 一. Java技术基础.................................................................... ...
随机推荐
- js确认末尾字符算法挑战
检查一个字符串(str)是否以指定的字符串(target)结尾. 如果是,返回true;如果不是,返回false. 这个挑战可以通过在ES2015中引入的.endsWith()方法来解决.但是出于这个 ...
- centos 搭建本地YUM源并使用apache共享YUM源
搭建本地YUM源 1.挂载镜像 2.搭建本地YUM源 删除多余repo文件保留一个就行 本地YUM源就搭建好了 yum repolist 查看yum源 3.使用apache共享YUM源 YUM服务器配 ...
- SSH工具
SSH是什么:SSH是一种网络协议,用于计算机之间的加密登录 应用:用来连接远程服务器 适用人员:需要操作服务器的程序员,linux管理员等 需要的基础知识: http://www.ee. ...
- 转 Tomcat访问日志详细配置
配置http访问日志.Tomcat自带的能够记录的http访问日志已经很详细了取消下面这段的注释: <Valve className="org.apache.catalina.valv ...
- 阿里云入选Gartner 2019 WAF魔力象限,唯一亚太厂商!
近期,在全球权威咨询机构Gartner发布的2019 Web应用防火墙魔力象限中,阿里云Web应用防火墙成功入围,是亚太地区唯一一家进入该魔力象限的厂商! Web应用防火墙,简称WAF.在保护Web应 ...
- Struts拦截器Interceptor
Struts2 拦截器 [Interceptor] 拦截器的工作原理如上图,每一个Action请求都包装在一系列的拦截器的内部.拦截器可以在Action执行直线做相似的操作也可以在Action执行直后 ...
- 洛谷P1364 医院设置(Floyd)
题目描述 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接点之间的距离为l.如上 ...
- 【Flutter学习】页面布局之列表和表格处理
一,概述 Flutter中拥有30多种预定义的布局widget,常用的有Container.Padding.Center.Flex.Row.Colum.ListView.GridView.按照< ...
- [Repost] 悬线法
<浅谈用极大化思想解决最大子矩形问题>作者:王知昆 首先,根据定理1:最大有效子矩形一定是一个极大子矩形.不过与前一种算法不同的是,我们不再要求每一次枚举的一定是极大子矩形而只要求所有的极 ...
- app = Flask(__name__) 是个什么东西
"""第一部分,初始化:所有的Flask都必须创建程序实例, web服务器使用wsgi协议,把客户端所有的请求都转发给这个程序实例 程序实例是Flask的对象,一般情况下 ...