黑马程序员——Java集合框架—Queue—BlockingQueue—ArrayBlockingQueue

来源:互联网 发布:java nextline 编辑:程序博客网 时间:2024/04/29 12:24

参考:

博客园:Java多线程-工具篇-BlockingQueue

红黑联盟:Java 7之多线程并发容器 - ArrayBlockingQueue(源码分析)

CSDN:余志强 Java5 多线程(八)-- ArrayBlockingQueue阻塞队列


ArrayBlockingQueue

引用参考文章中的一段话:

基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。


ArrayBlockingQueue在生产者放入数据和消费者获取数据,都是共用同一个锁对象,由此也意味着两者无法真正并行运行,这点尤其不同于LinkedBlockingQueue;按照实现原理来分析,ArrayBlockingQueue完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。Doug Lea之所以没这样去做,也许是因为ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。 ArrayBlockingQueue和LinkedBlockingQueue间还有一个明显的不同之处在于,前者在插入或删除元素时不会产生或销毁任何额外的对象实例,而后者则会生成一个额外的Node对象。这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。而在创建
ArrayBlockingQueue时,我们还可以控制对象的内部锁是否采用公平锁,默认采用非公平锁。


ArrayBlockingQueue队列的长度是固定的,一旦被创建,将无法改变队列的大小;队列的大小是通过构造函数指定的。


从ArrayBlockingQueue的构造方法可以看出,在创建其对象时,必须要指定队列的长度。


ArrayBlockingQueue实现了Collection中所有的方法,也就是说可以在ArrayBlockingQueue中调用它从Collection中继承的方法。同样可以使用Iterator来迭代ArrayBlockingQueue中的元素。

小示例:

package org.lgy.study.collection;import java.util.concurrent.BlockingQueue;import java.util.concurrent.ArrayBlockingQueue;/* javac -d classes "src/org/lgy/study/collection/ArrayBlockingQueueTest.java"java org.lgy.study.collection.ArrayBlockingQueueTest */public class ArrayBlockingQueueTest{public static void main(String[] args){// 阻塞队列,容量为1BlockingQueue<Object> bq = new ArrayBlockingQueue<>(1);// 创建3个生产者线程for(int i = 0; i < 3; i++){new Thread(new Producer(bq), "生产者-" + i).start();}// 创建1个消费者线程new Thread(new Consumer(bq), "消费者-" + 1).start();}// 生产者线程private static class Producer implements Runnable{private BlockingQueue<Object> bq;private Object obj;public Producer(BlockingQueue<Object> bq){this.bq = bq;}public void run(){// 每一个生产者线程都仅仅生产10个数据for(int i = 0; i < 10; i++){try{obj = new Object();// System.out.println(Thread.currentThread().getName() + ",即将放入数据... ...");bq.put(obj);  // 如果队列已满,则阻塞当前线程(即当前线程会停止在这里,不再继续往下执行)System.out.println(Thread.currentThread().getName() + obj);Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}}}}// 消费者线程private static class Consumer implements Runnable{private BlockingQueue<Object> bq;private Consumer(BlockingQueue<Object> bq){this.bq = bq;}public void run(){try{while(true){// 如果队列为空,则take方法会阻塞当前线程System.out.println(Thread.currentThread().getName() + bq.take());Thread.sleep(100);}}catch(InterruptedException e){e.printStackTrace();}}}}/* 结果:生产者-1java.lang.Object@70e25c99消费者-1java.lang.Object@70e25c99生产者-0java.lang.Object@5e70125b消费者-1java.lang.Object@5e70125b生产者-2java.lang.Object@3b68c294消费者-1java.lang.Object@3b68c294生产者-1java.lang.Object@7e25bcf5消费者-1java.lang.Object@7e25bcf5生产者-0java.lang.Object@50b7f7e5消费者-1java.lang.Object@50b7f7e5生产者-2java.lang.Object@3f575121消费者-1java.lang.Object@3f575121生产者-1java.lang.Object@7a1a37e9消费者-1java.lang.Object@7a1a37e9生产者-0java.lang.Object@5488792b消费者-1java.lang.Object@5488792b生产者-2java.lang.Object@7a55c8af消费者-1java.lang.Object@7a55c8af生产者-1java.lang.Object@3f18b6ce消费者-1java.lang.Object@3f18b6ce生产者-0java.lang.Object@118afdde消费者-1java.lang.Object@118afdde... ... */

问题一:死锁

看如下2段回导致死锁的代码:

生产者中的run方法:

public void run(){// 每一个生产者线程都仅仅生产10个数据for(int i = 0; i < 10; i++){try{synchronized(bq){obj = new Object();// System.out.println(Thread.currentThread().getName() + ",即将放入数据... ...");bq.put(obj);  // 如果队列已满,则阻塞当前线程(即当前线程会停止在这里,不再继续往下执行)System.out.println(Thread.currentThread().getName() + obj);}Thread.sleep(100);}catch(InterruptedException e){e.printStackTrace();}}}

消费者中的run方法:

public void run(){try{while(true){synchronized(bq){// 如果队列为空,则take方法会阻塞当前线程System.out.println(Thread.currentThread().getName() + bq.take());}Thread.sleep(100);}}catch(InterruptedException e){e.printStackTrace();}}

这2段代码与上面代码的区别仅仅是添加了synchronized代码块,添加synchronized代码块的初衷是想把一部分代码封装成一个原子操作,最终却导致了死锁。

死锁的原因如下:

对于生产者,程序首先会获取bq的锁,然后执行synchronized代码块,如果此时队列已满,调用put方法时将导致当前线程阻塞,当前现场就会停止在put方法处不再往下执行,从而导致不能释放bq的锁。

由于队列已满,所以应该有消费者take数据,消费者也先试图获取bq的锁,但由于bq的锁一直被某个生产者拿着,所以导致消费者进程阻塞,无法取数据。

最终产生了死锁。

0 0
原创粉丝点击