在实际编程中,会常用到JDK中Collection集合框架中的各类容器类如实现List,Map,Queue接口的容器类,可是这些容器类基本上不是线程安全的,除了使用Collections能够将其转换为线程安全的容器,Doug Lea大师为咱们都准备了对应的线程安全的容器,如实现List接口的CopyOnWriteArrayList(关于CopyOnWriteArrayList能够看这篇文章),实现Map接口的ConcurrentHashMap(关于ConcurrentHashMap能够看这篇文章),实现Queue接口的ConcurrentLinkedQueue(关于ConcurrentLinkedQueue能够看这篇文章)。java
最经常使用的"生产者-消费者"问题中,队列一般被视做线程间操做的数据容器,这样,能够对各个模块的业务功能进行解耦,生产者将“生产”出来的数据放置在数据容器中,而消费者仅仅只须要在“数据容器”中进行获取数据便可,这样生产者线程和消费者线程就可以进行解耦,只专一于本身的业务功能便可。阻塞队列(BlockingQueue)被普遍使用在“生产者-消费者”问题中,其缘由是BlockingQueue提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。编程
BlockingQueue基本操做总结以下(此图来源于JAVA API文档):数组
BlockingQueue继承于Queue接口,所以,对数据元素的基本操做有:安全
插入元素数据结构
true
,不然则返回false
。当队列满时不会抛出异常;删除元素框架
true
,不然为false
查看元素post
BlockingQueue具备的特殊操做:spa
插入数据:线程
删除数据code
实现BlockingQueue接口的有ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedTransferQueue, PriorityBlockingQueue, SynchronousQueue
,而这几种常见的阻塞队列也是在实际编程中会经常使用的,下面对这几种常见的阻塞队列进行说明:
1.ArrayBlockingQueue
ArrayBlockingQueue是由数组实现的有界阻塞队列。该队列命令元素FIFO(先进先出)。所以,对头元素时队列中存在时间最长的数据元素,而对尾数据则是当前队列最新的数据元素。ArrayBlockingQueue可做为“有界数据缓冲区”,生产者插入数据到队列容器中,并由消费者提取。ArrayBlockingQueue一旦建立,容量不能改变。
当队列容量满时,尝试将元素放入队列将致使操做阻塞;尝试从一个空队列中取一个元素也会一样阻塞。
ArrayBlockingQueue默认状况下不能保证线程访问队列的公平性,所谓公平性是指严格按照线程等待的绝对时间顺序,即最早等待的线程可以最早访问到ArrayBlockingQueue。而非公平性则是指访问ArrayBlockingQueue的顺序不是遵照严格的时间顺序,有可能存在,一旦ArrayBlockingQueue能够被访问时,长时间阻塞的线程依然没法访问到ArrayBlockingQueue。若是保证公平性,一般会下降吞吐量。若是须要得到公平性的ArrayBlockingQueue,可采用以下代码:
private static ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10,true);
复制代码
关于ArrayBlockingQueue的实现原理,能够看这篇文章。
2.LinkedBlockingQueue
LinkedBlockingQueue是用链表实现的有界阻塞队列,一样知足FIFO的特性,与ArrayBlockingQueue相比起来具备更高的吞吐量,为了防止LinkedBlockingQueue容量迅速增,损耗大量内存。一般在建立LinkedBlockingQueue对象时,会指定其大小,若是未指定,容量等于Integer.MAX_VALUE
3.PriorityBlockingQueue
PriorityBlockingQueue是一个支持优先级的无界阻塞队列。默认状况下元素采用天然顺序进行排序,也能够经过自定义类实现compareTo()方法来指定元素排序规则,或者初始化时经过构造器参数Comparator来指定排序规则。
4.SynchronousQueue
SynchronousQueue每一个插入操做必须等待另外一个线程进行相应的删除操做,所以,SynchronousQueue实际上没有存储任何数据元素,由于只有线程在删除数据时,其余线程才能插入数据,一样的,若是当前有线程在插入数据时,线程才能删除数据。SynchronousQueue也能够经过构造器参数来为其指定公平性。
5.LinkedTransferQueue
LinkedTransferQueue是一个由链表数据结构构成的无界阻塞队列,因为该队列实现了TransferQueue接口,与其余阻塞队列相比主要有如下不一样的方法:
transfer(E e) 若是当前有线程(消费者)正在调用take()方法或者可延时的poll()方法进行消费数据时,生产者线程能够调用transfer方法将数据传递给消费者线程。若是当前没有消费者线程的话,生产者线程就会将数据插入到队尾,直到有消费者可以进行消费才能退出;
tryTransfer(E e) tryTransfer方法若是当前有消费者线程(调用take方法或者具备超时特性的poll方法)正在消费数据的话,该方法能够将数据当即传送给消费者线程,若是当前没有消费者线程消费数据的话,就当即返回false
。所以,与transfer方法相比,transfer方法是必须等到有消费者线程消费数据时,生产者线程才可以返回。而tryTransfer方法可以当即返回结果退出。
tryTransfer(E e,long timeout,imeUnit unit)
与transfer基本功能同样,只是增长了超时特性,若是数据才规定的超时时间内没有消费者进行消费的话,就返回false
。
6.LinkedBlockingDeque
LinkedBlockingDeque是基于链表数据结构的有界阻塞双端队列,若是在建立对象时为指定大小时,其默认大小为Integer.MAX_VALUE。与LinkedBlockingQueue相比,主要的不一样点在于,LinkedBlockingDeque具备双端队列的特性。LinkedBlockingDeque基本操做以下图所示(来源于java文档)
如上图所示,LinkedBlockingDeque的基本操做能够分为四种类型:1.特殊状况,抛出异常;2.特殊状况,返回特殊值如null或者false;3.当线程不知足操做条件时,线程会被阻塞直至条件知足;4. 操做具备超时特性。
另外,LinkedBlockingDeque实现了BlockingDueue接口而LinkedBlockingQueue实现的是BlockingQueue,这两个接口的主要区别以下图所示(来源于java文档):
从上图能够看出,两个接口的功能是能够等价使用的,好比BlockingQueue的add方法和BlockingDeque的addLast方法的功能是同样的。
7.DelayQueue
DelayQueue是一个存放实现Delayed接口的数据的无界阻塞队列,只有当数据对象的延时时间达到时才能插入到队列进行存储。若是当前全部的数据都尚未达到建立时所指定的延时期,则队列没有队头,而且线程经过poll等方法获取数据元素则返回null。所谓数据延时期满时,则是经过Delayed接口的getDelay(TimeUnit.NANOSECONDS)
来进行断定,若是该方法返回的是小于等于0则说明该数据元素的延时期已满。