深切怀念传智播客张孝祥老师,特将其代表作——Java并发库视频研读两遍,受益颇丰,记以后阅
18.java5阻塞队列的应用
队列包含固定长度的队列和不固定长度的队列,先进先出
固定长度的队列往里放数据,如果放满了还要放,阻塞式队列就会等待,直到有数据取出,空出位置后才继续放;非阻塞式队列不能等待就只能报错了。
讲Condition时提到了阻塞队列的原理,Java中已经实现了阻塞队列ArrayBlockingQueue
BlockingQueue<E> public interfaceBlockingQueue<E>extends Queue<E>
支持两个附加操作的Queue,这两个操作是:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。
BlockingQueue 方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,第二种是返回一个特殊值(null 或 false,具体取决于操作),第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。下表中总结了这些方法:
|
抛出异常 |
特殊值 |
阻塞 |
超时 |
插入 |
add(e) |
offer(e) |
put(e) |
offer(e, time, unit) |
移除 |
remove() |
poll() |
take() |
poll(time, unit) |
检查 |
element() |
peek() |
不可用 |
不可用 |
BlockingQueue 不接受 null 元素。试图 add、put 或offer 一个 null 元素时,某些实现会抛出 NullPointerException。null 被用作指示poll 操作失败的警戒值。
BlockingQueue 可以是限定容量的。它在任意给定时间都可以有一个 remainingCapacity,超出此容量,便无法无阻塞地 put 附加元素。没有任何内部容量约束的 BlockingQueue 总是报告 Integer.MAX_VALUE 的剩余容量。
BlockingQueue 实现主要用于生产者-使用者队列,但它另外还支持 Collection 接口。因此,举例来说,使用remove(x) 从队列中移除任意一个元素是有可能的。然而,这种操作通常不会有效执行,只能有计划地偶尔使用,比如在取消排队信息时。
BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁或其他形式的并发控制来自动达到它们的目的。然而,大量的 Collection 操作(addAll、containsAll、retainAll 和 removeAll)没有 必要自动执行,除非在实现中特别说明。因此,举例来说,在只添加了c 中的一些元素后,addAll(c) 有可能失败(抛出一个异常)。
java.util.concurrent.ArrayBlockingQueue<E> E - 在此 collection 中保持的元素类型
extends AbstractQueue<E>implementsBlockingQueue<E>,Serializable
一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。
此类及其迭代器实现了Collection 和Iterator 接口的所有可选方法。
此类是JavaCollections Framework 的成员。
构造方法摘要 |
|
ArrayBlockingQueue(int capacity) 创建一个带有给定的(固定)容量和默认访问策略的 ArrayBlockingQueue。 |
|
ArrayBlockingQueue(int capacity, boolean fair) 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue。 |
|
ArrayBlockingQueue(int capacity, boolean fair,Collection<? extendsE> c) 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue,它最初包含给定 collection 的元素,并以 collection 迭代器的遍历顺序添加元素。 |
|
方法摘要 |
|
boolean |
add(E e) 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException。 |
void |
clear() 自动移除此队列中的所有元素。 |
boolean |
contains(Object o) 如果此队列包含指定的元素,则返回 true。 |
int |
drainTo(Collection<? superE> c) 移除此队列中所有可用的元素,并将它们添加到给定 collection 中。 |
int |
drainTo(Collection<? superE> c, int maxElements) 最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中。 |
Iterator<E> |
iterator() 返回在此队列中的元素上按适当顺序进行迭代的迭代器。 |
boolean |
offer(E e) 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false。 |
boolean |
offer(E e, long timeout,TimeUnit unit) 将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间。 |
E |
peek() 获取但不移除此队列的头;如果此队列为空,则返回 null。 |
E |
poll() 获取并移除此队列的头,如果此队列为空,则返回 null。 |
E |
poll(long timeout,TimeUnit unit) 获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。 |
void |
put(E e) 将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。 |
int |
remainingCapacity() 返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的其他元素数量。 |
boolean |
remove(Object o) 从此队列中移除指定元素的单个实例(如果存在)。 |
int |
size() 返回此队列中元素的数量。 |
E |
take() 获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。 |
Object[] |
toArray() 返回一个按适当顺序包含此队列中所有元素的数组。 |
<T> T[] |
toArray(T[] a) 返回一个按适当顺序包含此队列中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 |
String |
toString() 返回此 collection 的字符串表示形式。 |
阻塞队列的实现原理(Condition锁中有提到await signal)
public class BlockingQueueTest {
publicstatic void main(String[] args) {
finalBlockingQueue queue = new ArrayBlockingQueue(3);
for(inti=0;i<2;i++){
newThread(){
publicvoid run(){
while(true){
try{
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName()+ "准备放数据!");
queue.put(1);
System.out.println(Thread.currentThread().getName()+ "已经放了数据,"+
"队列目前有" + queue.size() + "个数据");
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
newThread(){
publicvoid run(){
while(true){
try{
//将此处的睡眠时间分别改为100和1000,观察运行结果
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+ "准备取数据!");
queue.take();
System.out.println(Thread.currentThread().getName()+ "已经取走数据,"+
"队列目前有" + queue.size() + "个数据");
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
public class BlockingQueueTest { public static void main(String[] args) { final BlockingQueue queue = new ArrayBlockingQueue(3); for(int i=0;i<2;i++){ new Thread(){ public void run(){ while(true){ try { Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName() + "准备放数据!"); queue.put(1); System.out.println(Thread.currentThread().getName() + "已经放了数据," + "队列目前有" + queue.size() + "个数据"); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } new Thread(){ public void run(){ while(true){ try { //将此处的睡眠时间分别改为100和1000,观察运行结果 Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "准备取数据!"); queue.take(); System.out.println(Thread.currentThread().getName() + "已经取走数据," + "队列目前有" + queue.size() + "个数据"); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } }
使用阻塞队列实现同步通信
public class BlockingQueueCommunication {
/**
* @param args
*/
publicstatic void main(String[] args) {
finalBusiness business = new Business();
newThread(
newRunnable() {
@Override
publicvoid run() {
for(inti=1;i<=50;i++){
business.sub(i);
}
}
}
).start();
for(inti=1;i<=50;i++){
business.main(i);
}
}
static class Business {
BlockingQueue<Integer> queue1 = newArrayBlockingQueue<Integer>(1);
BlockingQueue<Integer> queue2 = newArrayBlockingQueue<Integer>(1);
{
try {
System.out.println("xxxxxdfsdsafdsa");
queue2.put(1);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
public void sub(int i){
try{
queue1.put(1);
}catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(intj=1;j<=10;j++){
System.out.println("subthread sequece of " + j + ",loop of " + i);
}
try{
queue2.take();
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
public void main(int i){
try{
queue2.put(1);
}catch (InterruptedException e1) {
//TODO Auto-generated catch block
e1.printStackTrace();
}
for(intj=1;j<=100;j++){
System.out.println("mainthread sequece of " + j + ",loop of " + i);
}
try{
queue1.take();
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class BlockingQueueCommunication { /** * @param args */ public static void main(String[] args) { final Business business = new Business(); new Thread( new Runnable() { @Override public void run() { for(int i=1;i<=50;i++){ business.sub(i); } } } ).start(); for(int i=1;i<=50;i++){ business.main(i); } } static class Business { BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1); BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1); { Collections.synchronizedMap(null); try { System.out.println("xxxxxdfsdsafdsa"); queue2.put(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void sub(int i){ try { queue1.put(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(int j=1;j<=10;j++){ System.out.println("sub thread sequece of " + j + ",loop of " + i); } try { queue2.take(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void main(int i){ try { queue2.put(1); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } for(int j=1;j<=100;j++){ System.out.println("main thread sequece of " + j + ",loop of " + i); } try { queue1.take(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }