java 高并发编程之LinkedBlockingQueue讲解
一、LinkedBlockingQueue介绍
①、LinkedBlockingQueue是一个单向链表实现的阻塞队列,先进先出的顺序。支持多线程并发操作。相比于数组实现的ArrayBlockingQueue的有界,LinkedBlockingQueue可认为是无界队列。多用于任务队列。
②、LinkedBlockingQueue不同于ArrayBlockingQueue,它如果不指定容量,默认为Integer.MAX_VALUE,也就是无界队列。所以为了避免队列过大造成机器负载或者内存爆满的情况出现,我们在使用的时候建议手动传一个队列的大小,即在构造一个ArrayBlockingQueue对象时在其构造方法附上一个capacity( new LinkedBlockingQueue(5) )。
二、LinkedBlockingQueue常用方法介绍
1、clear() :从队列彻底移除所有元素。
2、iterator() : 返回在队列中的元素上按适当顺序进行迭代的迭代器,返回值为 Iterator。
3、offer(E e) :将指定元素插入到此队列的尾部(如果立即可行且不会超出此队列的容量),在成功时返回 true,如果此队列已满,则返回 false。
4、offer(E e, long timeout, TimeUnit unit) :将指定元素插入到此队列的尾部,如有必要,则等待指定的时间以使空间变得可用。
5、peek() : 获取但不移除此队列的头;如果此队列为空,则返回 null。
6、poll() :获取并移除此队列的头,如果此队列为空,则返回 null。
7、poll(long timeout, TimeUnit unit) :获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。
8、put(E e) :将指定元素插入到此队列的尾部,如有必要,则等待空间变得可用。
9、remainingCapacity() :返回理想情况下(没有内存和资源约束)此队列可接受并且不会被阻塞的附加元素数量。
10、remove(Object o) :从此队列移除指定元素的单个实例(如果存在),返回值为Boolean。
11、size() :返回队列中的元素个数。
12、take() :获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
13、Object[] toArray() :返回按适当顺序包含此队列中所有元素的数组。
14、toString() :返回此 collection 的字符串表示形式,返回值为String。
三、对LinkedBlockingQueue的一些方法写的一些Java代码示例
①、LinkedBlockingQueue是一个单向链表实现的阻塞队列,先进先出的顺序。支持多线程并发操作。相比于数组实现的ArrayBlockingQueue的有界,LinkedBlockingQueue可认为是无界队列。多用于任务队列。
②、LinkedBlockingQueue不同于ArrayBlockingQueue,它如果不指定容量,默认为Integer.MAX_VALUE,也就是无界队列。所以为了避免队列过大造成机器负载或者内存爆满的情况出现,我们在使用的时候建议手动传一个队列的大小,即在构造一个ArrayBlockingQueue对象时在其构造方法附上一个capacity( new LinkedBlockingQueue(5) )。
二、LinkedBlockingQueue常用方法介绍
1、clear() :从队列彻底移除所有元素。
2、iterator() : 返回在队列中的元素上按适当顺序进行迭代的迭代器,返回值为 Iterator。
3、offer(E e) :将指定元素插入到此队列的尾部(如果立即可行且不会超出此队列的容量),在成功时返回 true,如果此队列已满,则返回 false。
4、offer(E e, long timeout, TimeUnit unit) :将指定元素插入到此队列的尾部,如有必要,则等待指定的时间以使空间变得可用。
5、peek() : 获取但不移除此队列的头;如果此队列为空,则返回 null。
6、poll() :获取并移除此队列的头,如果此队列为空,则返回 null。
7、poll(long timeout, TimeUnit unit) :获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。
8、put(E e) :将指定元素插入到此队列的尾部,如有必要,则等待空间变得可用。
9、remainingCapacity() :返回理想情况下(没有内存和资源约束)此队列可接受并且不会被阻塞的附加元素数量。
10、remove(Object o) :从此队列移除指定元素的单个实例(如果存在),返回值为Boolean。
11、size() :返回队列中的元素个数。
12、take() :获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
13、Object[] toArray() :返回按适当顺序包含此队列中所有元素的数组。
14、toString() :返回此 collection 的字符串表示形式,返回值为String。
三、对LinkedBlockingQueue的一些方法写的一些Java代码示例
package chapter3.linkedblockingqueue; import java.util.Iterator; import java.util.concurrent.LinkedBlockingQueue; /** * @author czd */ public class LinkedBlockingQueueTest { public static void main(String[] args) { /** * 1、LinkedBlockingQueue(int capacity): * 创建一个具有给定(固定)容量的 LinkedBlockingQueue * * 2、LinkedBlockingQueue() : * 创建一个容量为 Integer.MAX_VALUE 的 LinkedBlockingQueue,无边界 */ LinkedBlockingQueue<Integer> linkedBlockingQueue = new LinkedBlockingQueue(5); //1、put(E e):将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间,无返回值 try { linkedBlockingQueue.put(6); System.out.println("put后:" + linkedBlockingQueue.peek()); } catch (InterruptedException e) { e.printStackTrace(); } //2、peek():将指定元素插入到此队列的尾部,如有必要,则等待空间变得可用。 Integer peekResult = linkedBlockingQueue.peek(); System.out.println("peekResult: " + peekResult); /** * 3.1、poll():获取并移除此队列的头,如果此队列为空,则返回 null。 * 3.2、poll(long timeout, TimeUnit unit):获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。 */ Integer pollResult = linkedBlockingQueue.poll(); System.out.println("pollResult: " + pollResult); Integer afterPollResult = linkedBlockingQueue.poll(); System.out.println("poll后的结果: " + afterPollResult); /** * 4.1 offer(E e) : 将指定元素插入到此队列的尾部(如果立即可行且不会超出此队列的容量), * 在成功时返回 true,如果此队列已满,则返回 false。 * * 4.2 offer(E e, long timeout, TimeUnit unit):将指定元素插入到此队列的尾部, * 如有必要,则等待指定的时间以使空间变得可用 */ Boolean offerBoolean = linkedBlockingQueue.offer(7); System.out.println("是否成功offer: " + offerBoolean); //5、remove(Objec8t o): 从此队列移除指定元素的单个实例(如果存在)。。返回值为Boolean Boolean remove8Boolean = linkedBlockingQueue.remove(8); System.out.println("移除8是否成功:" + remove8Boolean); Boolean remove7Boolean = linkedBlockingQueue.remove(7); System.out.println("移除7是否成功:" + remove7Boolean); //6、size():返回此队列中元素的数量。 try { linkedBlockingQueue.put(20); } catch (InterruptedException e) { e.printStackTrace(); } int sizeQueue = linkedBlockingQueue.size(); System.out.println("arrayBlockingQueue.size = " + sizeQueue); //7、remainingCapacity():返回理想情况下(没有内存和资源约束)此队列可接受并且不会被阻塞的附加元素数量 try { linkedBlockingQueue.put(8); linkedBlockingQueue.put(9); linkedBlockingQueue.put(10); Integer numbers = linkedBlockingQueue.remainingCapacity(); System.out.println("此队列可接受并且不会被阻塞的附加元素数量: " + numbers); } catch (InterruptedException e) { e.printStackTrace(); } //8、iterator() 返回在此队列中的元素上按适当顺序进行迭代的迭代器,返回值为Iterator<E> Iterator<Integer> iterator = linkedBlockingQueue.iterator(); while (iterator.hasNext()){ Integer result = iterator.next(); System.out.println("iterator的结果result: " + result); } //9、take():获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。 try { Integer takeResult = linkedBlockingQueue.take(); System.out.println("takeResult: " + takeResult); } catch (InterruptedException e) { e.printStackTrace(); } //10、clear(): 从队列彻底移除所有元素。 linkedBlockingQueue.clear(); System.out.println("看一下是否还存在元素:" + linkedBlockingQueue.peek()); } }
四、总结
1、LinkedBlockingQueue是一个阻塞队列,内部由两个ReentrantLock来实现出入队列的线程安全,由各自的Condition对象的await和signal来实现等待和唤醒功能。
2、LinkedBlockingQueue与ArrayBlockingQueue的区别在于:
①、队列大小有所不同,ArrayBlockingQueue是有界的初始化必须指定大小,而LinkedBlockingQueue可以是有界的也可以是无界的(Integer.MAX_VALUE),对于后者而言,当添加速度大于移除速度时,在无界的情况下,可能会造成内存溢出等问题。
②、数据存储容器不同,ArrayBlockingQueue采用的是数组作为数据存储容器,而LinkedBlockingQueue采用的则是以Node节点作为连接对象的链表。
③、由于ArrayBlockingQueue采用的是数组的存储容器,因此在插入或删除元素时不会产生或销毁任何额外的对象实例,而LinkedBlockingQueue则会生成一个额外的Node对象。这可能在长时间内需要高效并发地处理大批量数据的时,对于GC可能存在较大影响。
④、两者的实现队列添加或移除的锁不一样,ArrayBlockingQueue实现的队列中的锁是没有分离的,即添加操作和移除操作采用的同一个ReenterLock锁,而LinkedBlockingQueue实现的队列中的锁是分离的,其添加采用的是putLock,移除采用的则是takeLock,这样能大大提高队列的吞吐量,也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。