concrrent类下 BlockingDeque 下 自己实现代码编写
一、LinkedBlockingDeque简介
java6增加了两种容器类型,Deque和BlockingDeque,它们分别对Queue和BlockingQueue进行了扩展。
Deque是一个双端队列,deque(双端队列) 是 "Double Ended Queue" 的缩写。因此,双端队列是一个你可以从任意一端插入或者抽取元素的队列。实现了在队列头和队列尾的高效插入和移除。
BlockingDeque 类是一个双端队列,在不能够插入元素时,它将阻塞住试图插入元素的线程;在不能够抽取元素时,它将阻塞住试图抽取的线程。
正如阻塞队列使用与生产者-消费者模式,
双端队列同样适用于另一种相关模式,即工作密取。在生产者-消费者设计中,所有消费者有一个共享的工作队列,而在工作密取设计中,每个消费者都有各自的双端队列。如果一个消费者完成了自己双端队列中的全部工作,那么它可以从其它消费者双端队列末尾秘密地获取工作。密取工作模式比传统的生产者-消费者模式具有更高的可伸缩性,这是因为工作者线程不会在单个共享的任务队列上发生竞争。在大多数时候,它们都只是访问自己的双端队列,从而极大地减少了竞争。当工作者线程需要访问另一个队列时,它会从队列的尾部而不是头部获取工作,因此进一步降低了队列上的竞争程度。
LinkedBlockingDeque是双向链表实现的双向并发阻塞队列。该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(插入/删除);并且,该阻塞队列是支持线程安全。
此外,LinkedBlockingDeque还是可选容量的(防止过度膨胀),即可以指定队列的容量。如果不指定,默认容量大小等于Integer.MAX_VALUE。
BlockingDeque 的使用
在线程既是一个队列的生产者又是这个队列的消费者的时候可以使用到 BlockingDeque。如果生产者线程需要在队列的两端都可以插入数据,消费者线程需要在队列的两端都可以移除数据,这个时候也可以使用 BlockingDeque。BlockingDeque 图解:
一个 BlockingDeque - 线程在双端队列的两端都可以插入和提取元素。
一个线程生产元素,并把它们插入到队列的任意一端。如果双端队列已满,插入线程将被阻塞,直到一个移除线程从该队列中移出了一个元素。如果双端队列为空,移除线程将被阻塞,直到一个插入线程向该队列插入了一个新元素。
BlockingDeque 的方法
BlockingDeque 具有 4 组不同的方法用于插入、移除以及对双端队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下:
抛异常 | 特定值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | addFirst(o) | offerFirst(o) | putFirst(o) | offerFirst(o, timeout, timeunit) |
移除 | removeFirst(o) | pollFirst(o) | takeFirst(o) | pollFirst(timeout, timeunit) |
检查 | getFirst(o) | peekFirst(o) |
抛异常 | 特定值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | addLast(o) | offerLast(o) | putLast(o) | offerLast(o, timeout, timeunit) |
移除 | removeLast(o) | pollLast(o) | takeLast(o) | pollLast(timeout, timeunit) |
检查 | getLast(o) | peekLast(o) |
四组不同的行为方式解释:
- 抛异常:如果试图的操作无法立即执行,抛一个异常。
- 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
- 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
- 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。
BlockingDeque 继承自 BlockingQueue
BlockingDeque 接口继承自 BlockingQueue 接口。这就意味着你可以像使用一个 BlockingQueue 那样使用 BlockingDeque。如果你这么干的话,各种插入方法将会把新元素添加到双端队列的尾端,而移除方法将会把双端队列的首端的元素移除。正如 BlockingQueue 接口的插入和移除方法一样。
以下是 BlockingDeque 对 BlockingQueue 接口的方法的具体内部实现:
BlockingQueue | BlockingDeque |
---|---|
add() | addLast() |
offer() x 2 | offerLast() x 2 |
put() | putLast() |
remove() | removeFirst() |
poll() x 2 | pollFirst() |
take() | takeFirst() |
element() | getFirst() |
peek() | peekFirst() |
二、LinkedBlockingDeque源码分析
2.1、LinkedBlockingDeque的lock
LinkedBlockingDeque的原理就是使用一个可重入锁和这个锁生成的两个条件对象进行并发控制(classic two-condition algorithm)。LinkedBlockingDeque是一个带有长度的阻塞队列,初始化的时候可以指定队列长度(如果不指定就是Integer.MAX_VALUE),且指定长度之后不允许进行修改。
/** Main lock guarding all access */
final ReentrantLock lock = new ReentrantLock(); /** Condition for waiting takes */
private final Condition notEmpty = lock.newCondition(); /** Condition for waiting puts */
private final Condition notFull = lock.newCondition();
2.2、数据结构
双向链表
/** 双向链表节点 */
//ava里面static一般用来修饰成员变量或函数。但有一种特殊用法是用static修饰内部类,普通类是不允许声明为静态的,只有内部类才可以。被static修饰的内部类可以直接作为一个普通类来使用,而不需实例一个外部类
//final说明不允许其他类来继承这个类
static final class Node<E> {
/**
* 元素值
*/
E item; /**
* 节点前驱
* 1.指向前驱;2.指向this,说明前驱是尾节点,看unlinklast;3.指向null说明没有前驱
*/
Node<E> prev; /**
* 节点后继
* 1.指向后继;2.指向this,说明后继是头结点,看unlinkfirst;3.指向null说明没有后继
*/
Node<E> next; Node(E x) {
item = x;
}
}
2.3、成员变量
//first是双向链表的表头
transient Node<E> first; //不允许序列化 保证安全 //last是双向链表的表尾
transient Node<E> last; //count是LinkedBlockingDeque的实际大小,即双向链表中当前节点个数
private transient int count; //是LinkedBlockingDeque的容量,它是在创建LinkedBlockingDeque时指定的
private final int capacity;
2.4、构造函数
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
} public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
} public LinkedBlockingDeque(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock lock = this.lock;
lock.lock(); // Never contended, but necessary for visibility
try {
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (!linkLast(new Node<E>(e)))
throw new IllegalStateException("Deque full");
}
} finally {
lock.unlock();
}
}
2.5、入队
addFirst,addLast分别调用offerFirst,offerLast,而offerFirst,offerLast和putFirst,putLast都是调用了linkFirst和linkLast。
public void addFirst(E e) {
if (!offerFirst(e))
throw new IllegalStateException("Deque full");
} public void addLast(E e) {
if (!offerLast(e))
throw new IllegalStateException("Deque full");
} public boolean offerFirst(E e) {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkFirst(node);
} finally {
lock.unlock();
}
} public boolean offerLast(E e) {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkLast(node);
} finally {
lock.unlock();
}
} public void putFirst(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (!linkFirst(node))
notFull.await();
} finally {
lock.unlock();
}
} public void putLast(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
while (!linkLast(node))
notFull.await();
} finally {
lock.unlock();
}
}
linkFirst和linkLast
/**
* 设置node为链表头节点,链表满时为false
*/
private boolean linkFirst(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity) //超过容量false
return false;
Node<E> f = first;
node.next = f; //新节点的next指向原first
first = node; //设置node为新的first
if (last == null) //没有尾节点,就将node设置成尾节点
last = node;
else
f.prev = node; //有尾节点,那就将之前first的pre指向新增node
++count; //累加节点数量 //此处我在实现代码的过程中漏掉了
notEmpty.signal(); //有新节点入队,通知非空条件队列
return true;
} /**
* 设置node为链表尾节点,链表满时为false
*/
private boolean linkLast(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
Node<E> l = last;
node.prev = l;
last = node;
if (first == null) //为null,说明之前队列空吧,那就first也指向node
first = node;
else
l.next = node; //非null,说明之前的last有值,就将之前的last的next指向node
++count;
notEmpty.signal();
return true;
}
2.6、出队
public E removeFirst() {
E x = pollFirst();
if (x == null) throw new NoSuchElementException();
return x;
} public E removeLast() {
E x = pollLast();
if (x == null) throw new NoSuchElementException();
return x;
} public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
} finally {
lock.unlock();
}
} public E pollLast() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkLast();
} finally {
lock.unlock();
}
} public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
while ( (x = unlinkFirst()) == null)
notEmpty.await();
return x;
} finally {
lock.unlock();
}
} public E takeLast() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
while ( (x = unlinkLast()) == null)
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}
核心方法
/**
* 移除头结点,链表空返回null
*/
private E unlinkFirst() {
// assert lock.isHeldByCurrentThread();
Node<E> f = first;
if (f == null)
return null; //空返回null
Node<E> n = f.next;
E item = f.item;
f.item = null;
f.next = f; // help GC
first = n;
if (n == null) //说明之前应该只有一个节点,移除头结点后,链表空,现在first和last都指向null了
last = null;
else
n.prev = null; //否则的话,n的pre原来指向之前的first,现在n变为first了,pre指向null
--count;
notFull.signal(); //通知非满条件队列
return item;
} /**
* 移除尾结点,链表空返回null
*/
private E unlinkLast() {
// assert lock.isHeldByCurrentThread();
Node<E> l = last;
if (l == null)
return null;
Node<E> p = l.prev;
E item = l.item;
l.item = null;
l.prev = l; // help GC
last = p;
if (p == null)
first = null;
else
p.next = null;
--count;
notFull.signal();
return item;
} /**
* 移除指定节点:p--》x--》n
*/
void unlink(Node<E> x) {
// assert lock.isHeldByCurrentThread();
Node<E> p = x.prev;
Node<E> n = x.next;
if (p == null) { //prev为null说明x节点为头结点
unlinkFirst();
} else if (n == null) {
unlinkLast(); //nex为null说明待清除节点为尾节点
} else { //否则的话节点处于链表中间
p.next = n; //将p和n互链
n.prev = p;
x.item = null;
// 没有断开x节点链接,可能有其他线程在迭代链表
--count;
notFull.signal();
}
}
2.7、peek方法
public E peek() {
return peekFirst();
} public E peekFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (first == null) ? null : first.item;
} finally {
lock.unlock();
}
}
//peekLast就不贴了
2.8、size方法
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
三、JDK或开源框架中使用
四、使用示例
java.util.ArrayDeque 类提供了可调整大小的阵列,并实现了Deque接口。以下是关于阵列双端队列的要点:Java.util.ArrayDeque
数组双端队列没有容量限制,使他们增长为必要支持使用。
它们不是线程安全的;如果没有外部同步。
不支持多线程并发访问。
null元素被禁止使用在数组deques。
它们要比堆栈Stack和LinkedList快。
此类及其迭代器实现Collection和Iteratorinterfaces方法可选。
类的声明
以下是java.util.ArrayDeque类的声明:
public class ArrayDeque<E>
extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable
这里<E>代表一个元素,它可以是任何类。例如,如果你正在构建一个整数数组列表,那么初始化可为
ArrayList<Integer> list = new ArrayList<Integer>();
S.N. | 方法 & 描述 |
---|---|
1 | boolean add(E e) 此方法将添加指定的元素,在此deque队列的末尾。 |
2 | void addFirst(E e) 此方法将添加指定的元素,在此deque队列的前面。 |
3 | void addLast(E e) 此方法将插入指定的元素,在此deque队列的末尾。 |
4 | void clear() 此方法移除此deque队列的元素。 |
5 | ArrayDeque<E> clone() 此方法返回此deque队列的副本。 |
6 | boolean contains(Object o) 如果此deque 队列包含指定的元素,此方法返回true。 |
7 | Iterator<E> descendingIterator() 此方法返回一个迭代器在此deque队列以逆向顺序的元素。 |
8 | E element() 此方法检索,但是不移除此deque队列表示的队列的头部。 |
9 | E getFirst() 此方法检索,但是不移除此deque队列的第一个元素。 |
10 | E getLast() 此方法检索,但是不移除此deque队列的最后一个元素。 |
11 | boolean isEmpty() 如果此deque队列不包含元素,此方法返回true。 |
12 | Iterator<E> iterator() 此方法返回一个迭代器在此deque队列的元素。 |
13 | boolean offer(E e) 此方法将指定的元素,在此deque队列的末尾。 |
14 | boolean offerFirst(E e) 此方法将指定的元素,在此deque队列的前面。 |
15 | boolean offerLast(E e) 此方法将指定的元素,在此deque队列的末尾。 |
16 | E peek() 此方法检索,但是不移除此deque队列表示的队列的头部,如果此deque队列为空,则返回null。 |
17 | E peekFirst() 此方法检索,但是不移除此deque 队列的第一个元素,或者如果此deque 队列为空,则返回null。 |
18 | E peekLast() 此方法检索,但是不移除此deque队列的最后一个元素,如果此deque队列为空,则返回null。 |
19 | E poll() 此方法检索并移除此deque队列表示的队列的头部,如果此deque队列为空,则返回null。 |
20 | E pollFirst() 此方法检索并移除此deque队列的第一个元素,或者如果此deque队列为空,则返回null。 |
21 | E pollLast() 此方法检索并移除此deque队列的最后一个元素,如果此deque队列为空,则返回null。 |
22 | E pop() 这种方法的此deque队列所表示的堆栈弹出一个元素。 |
23 | void push(E e) 这种方法将元素推入此deque队列所表示的堆栈。 |
24 | E remove() 此方法检索并移除此deque队列表示的队列的头部。 |
25 | boolean remove(Object o) 此方法从此deque队列中移除指定元素的单个实例。 |
26 | E removeFirst() 此方法检索并移除此deque队列的第一个元素。 |
27 | boolean removeFirstOccurrence(Object o) 此方法移除此deque队列的指定元素的第一个匹配。 |
28 | E removeLast() 此方法检索并移除此deque队列的最后一个元素。 |
29 | boolean removeLastOccurrence(Object o) 此方法移除此deque队列的指定元素的最后一次出现。 |
30 | int size() 此方法返回在此deque队列的元素个数。 |
31 | object[] toArray() 这个方法返回一个包含所有在此deque队列在适当的序列中元素的数组。 |
concrrent类下 BlockingDeque 下 自己实现代码编写的更多相关文章
- test文件夹,测试类是放在src目录下的,test测试代码是代码啊,当然要放在代码文件夹下
test文件夹,测试类是放在src目录下的,test测试代码是代码啊,当然要放在代码文件夹下 Maven的标准工程结构 Maven的标准工程结构如下: |-- pom.xml(maven的核心配置文件 ...
- 巧用CSS3 :target 伪类制作Dropdown下拉菜单(无JS)
:target 是CSS3 中新增的一个伪类,用以匹配当前页面的URI中某个标志符的目标元素(比如说当前页面URL下添加#comment就会定位到id=“comment”的位置,俗称锚).CSS3 为 ...
- Siki_Unity_2-1_API常用方法和类详细讲解(下)
Unity 2-1 API常用方法和类详细讲解(下) 任务101&102:射线检测 射线origin + direction:射线检测:射线是否碰撞到物体 (物体需要有碰撞器),碰撞物体的信息 ...
- jdk1.8源码包下载并导入到开发环境下助推高质量代码(Eclipse、MyEclipse和Scala IDEA for Eclipse皆适用)(图文详解)
不多说,直接上干货! jdk1.8 源码, Linux的同学可以用的上. 由于源码JDK是前版本的超集, 所以1.4, 1.5, 1.6, 1.7都可以用的上. 其实大家安装的jdk路径下,这 ...
- [python]类与对象-下
[实例对象]可以简称为[实例] 一.类与对象的关系 [类]是[对象]的模板. [类]就像工厂的模具,以它为模板,造出来的成千上万的产品,才是被我们消费.购买.使用,真正融入我们生活的东西.这些产品,在 ...
- Linux下9种优秀的代码比对工具推荐
大家好,我是良许. 在我们编写代码的时候,我们经常需要知道两个文件之间,或者同一个文件不同版本之间有什么差异性.在 Windows 下有个很强大的工具叫作 BeyondCompare ,那在 Linu ...
- Python_selenium PO模式下 Tesecase 的相同执行代码做成selenium_base_case公共模块及调用
作用: PO模式下 Tesecase 的相同执行代码做成selenium_base_case公共模块及调用,提高代码简洁度,实现同样效果. 框架结构: 代码简单实践: common模块下 seleni ...
- 巧用CSS3:target 伪类制作Dropdown下拉菜单(无JS)
原文链接:http://devework.com/css3-target-dropdown.html :target 是CSS3 中新增的一个伪类,用以匹配当前页面的URI中某个标志符的目标元素(比如 ...
- c++(vs上)与g++(linux下)对于++操作的汇编代码解读
先来看一个代码,估计很多同学都碰到过其中的某一个. #include <stdio.h> #include <iostream> using namespace std; in ...
随机推荐
- Jenkins 忘记admin密码拯救方法
突然有一日发现自己忘记了jenkins的管理员密码,因为我一直登录的是另外一个非管理员账户.如果出现必须要使用管理员账户操作的,比如用户管理那里的,必须要管理员账号吧,这就尴尬了. 很方的我打开安装j ...
- 转:centos7搭建jenkins小记
转自:https://segmentfault.com/a/1190000007086764 安装java环境 1.查看服务器版本 centos7,继续. cat /etc/redhat-releas ...
- Android--解析XML之DOM
前言 前面已经介绍了Android平台下两种解析XML的方法,SAX和PULL,这两个均为事件驱动,以流的形式解析XML文档.现在介绍一种新的方式DOM方式解析XML. DOM是一种用于XML文档对象 ...
- Code Complete-13/7/23
What is "construction"? Hava u ever used construction paper to make some things?What i ...
- eclipse使用maven打包的时候发现静态资源没包含进去
今天在打包的时候,发现传上去的包里没有配置静态资源. (右键项目--->Run As ---> 8 Maven install ) 后来发现是因为在pom.xml里没有配置打包静态资源的 ...
- [转]Redis配置文件详解
本文转自http://blog.csdn.net/neubuffer/article/details/17003909 redis是一款开源的.高性能的键-值存储(key-value store),和 ...
- shiro源码篇 - 疑问解答与系列总结,你值得拥有
前言 开心一刻 小明的朋友骨折了,小明去他家里看他.他老婆很细心的为他换药,敷药,然后出去买菜.小明满脸羡慕地说:你特么真幸福啊,你老婆对你那么好!朋友哭得稀里哗啦的说:兄弟你别说了,我幸福个锤子,就 ...
- 解读经典-《C#高级编程》第七版-Chapter1-.Net体系结构-Page6-13
01 中间语言(IL) .Net中间语言(IL)的特性,很大程度上来自于要支持多语言互操作性.要支持多语言互操作性,是因为微软想搞一个大事情,将它的老产品线VB和VC++,VJ++都装入.Net架构中 ...
- OpenCV入门之获取图像的旋转角度
在我们的日常生活中,所碰到的图像往往都有一定的倾斜.那么,如何用OpenCV来获取图像的旋转角度呢? 我们以下面的图片为例,简单介绍如何用OpenCV来获取图像的旋转角度. 可以看到,该图 ...
- Web下的HTTPS应用
---------------------------------------------------------------------------------------------------- ...