Thread学习(九) 并发的Queen学习ArrayBlockingQueue,LinkedBlockingQueue

来源:互联网 发布:fresh玫瑰面膜 知乎 编辑:程序博客网 时间:2024/06/09 13:54

并发的Queue

在并发队列上,JDK实现了两套实现,一套是以ConcurrentLinkedQueue为代表的高性能非阻塞队列,另一套是以BlockingQueue接口为代表的阻塞队列,无论哪种都继承自Queue。
在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出)。Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。


1.阻塞队列和非阻塞队列的区别

阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列.

2.ConcurrentLinkedQueue为代表的高性能非阻塞队列

ConcurrentLinkedQueue是Queue的一个安全实现.Queue中元素按FIFO原则进行排序.采用CAS操作,来保证元素的一致性。他是一个高并发场景下的队列,通过无锁的方式实现了高并发状态下的高性能,通常情况下ConcurrentLinkedQueue性能要好于BlockingQueue,他是一个基于链接节点的无界安全队列。该队列不允许null元素。
ConcurrentLinkedQueue的重要方法
1.add()和offer()在ConcurrentLinkedQueue中都是向队列中添加元素。(在ConcurrentLinkedQueue中这两个方法没有任何区别,add()的源码是调用的offer()方法实现的
2.poll()和peek()都是取头元素节点,区别在于前者会删除元素,后者不会删除(也就是poll()在取得队列中头结点的同时会对这个节点进行删除操作)
public class QueenTest {public static void main(String[] args) {ConcurrentLinkedQueue<String> concurrentLinkedQueue = new ConcurrentLinkedQueue<String>();concurrentLinkedQueue.add("a");concurrentLinkedQueue.offer("b");concurrentLinkedQueue.offer("c");concurrentLinkedQueue.offer("d");concurrentLinkedQueue.offer("e");for (String string : concurrentLinkedQueue) {System.out.println(string);}}}

3.BlockingQueue接口阻塞队列

1.ArrayBlockingQueue

LinkedBlockingQueue是基于数组的阻塞队列实现,在它的内部维护了一个固定长度的数组,以便于缓存队列中的数据对象,其内部没有实现读写分离,也就意味着生产和消费不能完全并行,长度是需要定义的。也叫作有界队列,在很多场合非常适合使用。

2.LinkedBlockingQueue

一个基于已链接节点的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部 是在队列中时间最长的元素。队列的尾部 是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列检索操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。可选的容量范围构造方法参数作为防止队列过度扩展的一种方法。如果未指定容量,则它等于Integer.MAX_VALUE。除非插入节点会使队列超出容量,否则每次插入后会动态地创建链接节点。

区别一下几种方法:

1. offer(E e) offer(E e,long timeout,TimeUnit unit),put(E e),add(E e)

 (1)offer方法在添加元素时,如果发现队列已满无法添加的话,会直接返回false。

public class QueenTest {public static void main(String[] args) {LinkedBlockingQueue<String> q = new LinkedBlockingQueue<String>(5);System.out.println(q.offer("a"));System.out.println(q.offer("b"));System.out.println(q.offer("c"));System.out.println(q.offer("d"));System.out.println(q.offer("e"));System.out.println(q.offer("f"));}}
运行结果:

true
true
true
true
true
false

 (2)对于put方法,若向队尾添加元素的时候发现队列已经满了会发生阻塞一直等待空间,以加入元素。

public class QueenTest {public static void main(String[] args) {LinkedBlockingQueue<String> q = new LinkedBlockingQueue<String>(5);try {q.put("a");q.put("b");q.put("c");q.put("d");q.put("e");q.put("f");System.out.println(1111);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
我们会发现程序一直阻塞在q.put("f")那里,最后的1111不会输出

(3)add方法在添加元素的时候,正确返回true,若超出了队列的长度会直接抛出异常:

public class QueenTest {public static void main(String[] args) {LinkedBlockingQueue<String> q = new LinkedBlockingQueue<String>(5);q.add("a");q.add("b");q.add("c");q.add("d");q.add("e");q.add("f");}}
运行结果:Exception in thread "main" java.lang.IllegalStateException: Queue full
at java.util.AbstractQueue.add(AbstractQueue.java:98)
at com.lbm.Test.QueenTest.main(QueenTest.java:14)



2.poll() poll(long timeout,TimeUnit unit)和peek();

poll方法是 立刻或者等待指定时间后,获取并且移除队列的头。如果队列为空,则为null

peek方法是  获取但不移除此队列的头,如果此队列为空,则为null

必须要使用take()方法在获取队列元素的时候才能达成阻塞结果。



0 0