java同步包种ArrayBlockingQueue类的分析与理解
前言:
ArrayBlockingQueue类是一个堵塞队列。重要用于多线程操作的条件。
一,官方解释
一个建立在数组之上被BlockingQueue绑定的堵塞队列。这个队列元素顺序是先进先出。队列的头部是在队列中待的时间最长的元素。队列的尾部是再队列中待的时间最短的元素。新的元素会被插入到队列尾部,而且队列从队列头部获取元素。
这是一个典型的绑定缓冲,在这个缓冲区中。有一个固定大小的数组持有生产者插入的数据。而且消费者会提取这些数据。一旦这个类被创建,那么这个数组的容量将不能再被改变。尝试使用put操作给一个满队列插入元素将导致这个操作被堵塞;尝试从空队列中取元素也会被堵塞。
这个类推荐了一个可选的公平策略来排序等待的生产者和消费者线程。
默认的,这个顺序是不确定的。可是队列会使用公平的设置true来使线程依照先进先出顺序訪问。通常公平性会降低吞吐量可是却降低了可变性以及避免了线程饥饿。
这个类和它的迭代器实现了全部可选的Collection按Iterator接口的方法。
二。源代码分析
先来看看ArrayBlockingQueue类的特殊的一个构造器代码:
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
这个构造器中得參数fair大家注意到没有。正好是传给ReentrantLock的參数,而这个參数正好就是ReentrantLock决定是否为公平或者非公平队列的參数。ReentrantLock參考我的这篇关于java中ReentrantLock类的源代码分析以及总结与样例。
再往下看到,这两个notEmpty和notFull參数实际上是Condition。而Condition能够把它看做一个堵塞信号,Condition的子类ConditionObject(是AbstractQueuedSynchronizer的内部类)拥有两个方法signal和signalAll方法。前一个方法是唤醒队列中得第一个线程,而signalAll是唤醒队列中得全部等待线程,可是仅仅有一个等待的线程会被选择,这两个方法能够看做notify和notifyAll的变体。
在这个堵塞队列的insert和remove方法中都会被调用signal来唤醒等待线程,在put方法中。假设队列已经满了,则会调用await方法来。直到队列有空位,才会调用insert方法插入元素。源码例如以下:
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
notEmpty.signal();
}
事实上设计这个类的程序猿哥哥已经想到了假设我们要用这个类,可是不想在队列满了之后。再插入元素被堵塞。提供了offer方法。这个offer方法有重载方法,调用offer(E e)方法时,假设队列已经满了,那么会直接返回一个false,假设没有满,则直接调用insert插入到队列中;调用offer(E e, long timeout, TimeUnit unit)方法时,会在队列满了之后堵塞队列,可是这里能够由开发者设置超时时间,假设超时时队列还是满的,则会以false返回。源代码例如以下所看到的:
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException { checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
insert(e);
return true;
} finally {
lock.unlock();
}
}
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
}
插入数据有堵塞和非堵塞之分,那么提取数据也肯定就有堵塞与非堵塞之分了。
当中take方法是个堵塞方法,当队列为空时,就被堵塞。源代码例如以下:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}
方法poll是重载方法,跟offer相对,也有基础方法和超时方法之分。
在这个类中还提供了peek方法来提取数据。可是peek方法是从对了的tail提取,而pool是从队列的head提取,即peek提取的元素是进入队列最晚的,而pool提取的元素是进入队列最早时间最长的元素。
再来看看这个类中得迭代器。这个类中的迭代器是线程安全的,为什么会线程安全?由于在实现的next和remove方法中都加了lock了。安全性根本停不下来啊~上源代码:
private class Itr implements Iterator<E> {
private int remaining; // Number of elements yet to be returned
private int nextIndex; // Index of element to be returned by next
private E nextItem; // Element to be returned by next call to next
private E lastItem; // Element returned by last call to next
private int lastRet; // Index of last element returned, or -1 if none
Itr() {
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
lastRet = -1;
if ((remaining = count) > 0)
nextItem = itemAt(nextIndex = takeIndex);
} finally {
lock.unlock();
}
}
public boolean hasNext() {
return remaining > 0;
}
public E next() {
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock(); //锁定
try {
if (remaining <= 0)
throw new NoSuchElementException();
lastRet = nextIndex;
E x = itemAt(nextIndex); // check for fresher value
if (x == null) {
x = nextItem; // we are forced to report old value
lastItem = null; // but ensure remove fails
}
else
lastItem = x;
while (--remaining > 0 && // skip over nulls
(nextItem = itemAt(nextIndex = inc(nextIndex))) == null)
;
return x;
} finally {
lock.unlock();
}
}
public void remove() {
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
int i = lastRet;
if (i == -1)
throw new IllegalStateException();
lastRet = -1;
E x = lastItem;
lastItem = null;
// only remove if item still at index
if (x != null && x == items[i]) {
boolean removingHead = (i == takeIndex);
removeAt(i);
if (!removingHead)
nextIndex = dec(nextIndex);
}
} finally {
lock.unlock();
}
}
}
三,总结
对这个类的描写叙述已经代码分析完毕了,接下来我们来总结一下这个类的一些特点吧:
1.一旦创建,则容量不能再修改
2.这个类是线程安全的,而且迭代器也是线程安全的
3.这个类的put和take方法分别会在队列满了和队列空了之后被堵塞操作。
4.这个类提供了offer和poll方法来插入和提取元素,而不会在队列满了或者队列为空时堵塞操作。
5.这个队列的锁默认是不公平策略,即唤醒线程的顺序是不确定的。
java同步包种ArrayBlockingQueue类的分析与理解的更多相关文章
- 死磕 java集合之ArrayBlockingQueue源码分析
问题 (1)ArrayBlockingQueue的实现方式? (2)ArrayBlockingQueue是否需要扩容? (3)ArrayBlockingQueue有什么缺点? 简介 ArrayBloc ...
- java多线程系列(九)---ArrayBlockingQueue源码分析
java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...
- 并发编程(八)—— Java 并发队列 BlockingQueue 实现之 ArrayBlockingQueue 源码分析
开篇先介绍下 BlockingQueue 这个接口的规则,后面再看其实现. 阻塞队列概要 阻塞队列与我们平常接触的普通队列(LinkedList或ArrayList等)的最大不同点,在于阻塞队列的阻塞 ...
- Java线程池ThreadPoolExecutor类源码分析
前面我们在java线程池ThreadPoolExecutor类使用详解中对ThreadPoolExector线程池类的使用进行了详细阐述,这篇文章我们对其具体的源码进行一下分析和总结: 首先我们看下T ...
- 《手把手教你》系列技巧篇(七十一)-java+ selenium自动化测试-自定义类解决元素同步问题(详解教程)
1.简介 前面宏哥介绍了几种关于时间等待的方法,也提到了,在实际自动化测试脚本开发过程,百分之90的报错是和元素因为时间不同步而发生报错.本文介绍如何新建一个自定义的类库来解决这个元素同步问题.这样, ...
- Java LinkedBlockingQueue和ArrayBlockingQueue分析
LinkedBlockingQueue是一个链表实现的堵塞队列,在链表一头增加元素,假设队列满.就会堵塞.还有一头取出元素.假设队列为空.就会堵塞. LinkedBlockingQueue内部使用Re ...
- Java线程池ThreadPoolExecutor使用和分析(一)
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- java并发包&线程池原理分析&锁的深度化
java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...
- MapReduce剖析笔记之八: Map输出数据的处理类MapOutputBuffer分析
在上一节我们分析了Child子进程启动,处理Map.Reduce任务的主要过程,但对于一些细节没有分析,这一节主要对MapOutputBuffer这个关键类进行分析. MapOutputBuffer顾 ...
随机推荐
- php中命名空间和use
php中命名空间和use 总结 php中的namespace就有点像java中package包的概念 php中的use的概念就是用别人的命名空间中的类 php中的include enquire是引入文 ...
- centos7 安装swftools Apache_OpenOffice
centos7 yum -y install wget wget http://www.swftools.org/swftools-0.9.2.tar.gz tar -xf swftools-.tar ...
- Hive框架基础(一)
* Hive框架基础(一) 一句话:学习Hive有毛用? 那么解释一下 毛用: * 操作接口采用类SQL语法,提供快速开发的能力(不会Java也可以玩运算) * 避免了去写MapReduce,减少开发 ...
- Android属性动画-Interpolator和ViewPropertyAnimator的用法
Interpolator的用法 Interpolator这个东西很难进行翻译,直译过来的话是补间器的意思,它的主要作用是可以控制动画的变化速率,比如去实现一种非线性运动的动画效果.那么什么叫做非线性运 ...
- UNIX 是什么?怎么诞生的?
要记住, 当一扇门在你面前关闭的时候, 另一扇门就会打开. 肯·汤普森(Ken Thompson) 和丹尼斯·里奇(Dennis Richie)两个人就是这句名言很好的实例.他们俩是20世纪最优秀的信 ...
- 欧拉函数 euler
O - 找新朋友 1.欧拉函数 euler() 在数论,对正整数n,欧拉函数是 少于或等于n的数中与n 互质 的数的数目. 互质:公约数只有 1 的两个整数,称为互质整数.即 最大的公约数也就是 ...
- 【Educational Codeforces Round 37 C】 Swap Adjacent Elements
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 显然l..r这一段连续的1可以把l..r+1变成有序的. 那么就把所有的连续1段变成有序的就好. 看看最后是不是升序即可. [代码] ...
- CodeForces 363B Fence
Fence Time Limit: 1000ms Memory Limit: 262144KB This problem will be judged on CodeForces. Original ...
- JAVA JS 中的 modulus exponent 生成 C# 公匙
C#用的是xml,里面是base64编码的.你上面的就是hex格式,只要把上面hex格式转成byte数组,然后在base64编码就可以了. public static byte[] Hex2Byte( ...
- 洛谷 P1209 [USACO1.3]修理牛棚 Barn Repair
P1209 [USACO1.3]修理牛棚 Barn Repair 题目描述 在一个夜黑风高,下着暴风雨的夜晚,farmer John的牛棚的屋顶.门被吹飞了. 好在许多牛正在度假,所以牛棚没有住满. ...