阻塞队列- 基础知识
BlockingQueue 的核心方法
放入数据:
-
offer(anObject): 表示如果可能的话,将 anObject 加到 BlockingQueue 里。即如果 BlockingQueue 可以容纳,则返回 true,否则返回 false 。(本方法不阻塞当前执行方法的线程)
-
offer(E o,long timeout,TimeUnit unitz): 可以设定等待时间。如果在指定的时间内还不能往队伍中加入 BlockingQueue,则返回失败。
-
put(anObject): 将 anObject 加到 BlockingQueue 里,如果 BlockingQueue 没有空间,则调用此方法的线程被阻断,直到 BlockingQueue 里面有空间再继续。
获取数据:
-
poll(long timeout,TimeUnit unit): 从 BlockingQueue 中取出一个队首的对象。如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据:否则知道时间超时还没有数据可取,返回失败。
-
take(): 取走 BlockingQueue 里排在首位的对象。若 BlockingQueue 为空,则阻断进入等待状态,知道 BlockingQueue 有新的数据被加入。
-
drainTo(): 一次性从 BlockingQueue 获取所有可用的数据对象(还可以指定获取数据的个数)。通过该方法,可以提升获取数据的效率:无须多次分批加锁或释放锁。
Java中的7个阻塞队列
- ArrayBockingQueue: 由数据结构组成的有界阻塞队列。
- LinkedBlockingQueue: 由链表结构组成的有界阻塞队列。
- PriorityBlockingQueue: 支持优先级排序的无界阻塞队列。
- DelayQueue: 使用优先级队列实现的无界阻塞队列。
- SynchronousQueue: 不存储元素的阻塞队列。
- LinkedTransferQueue: 由链表结构组成的无界阻塞队列。
- LinkedBlockingDeque: 由链表结构组成的双向阻塞队列。
ArrayBlockingQueu 和 LinkedBlockingQueue
ArrayBlockingQueu:是用数组实现的有界阻塞队列,并按照先进先出(FIFO)的原则对元素进行排序。
//第一个参数是容量 第二个参数是是否要保证线程的公平地访问队列(默认是false)
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(200,true);
LinkedBlockingQueue: 是基于链表的阻塞队列,同 ArrayBlockingQueu类似。
那他们区别在哪?
-
队伍中锁的实现不同
ArrayBlockingQueue 实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁。
LinkedBlockingQueue 实现的队列中锁是分离的,即生产用的是 putLock,消费是 takeLock。 -
在生产或消费是操作不同
ArrayBlockingQueue 实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的。
LinkedBlockingQueue 实现的队列中在生产和消费的时候,需要把枚举对象转换为 Node 进行插入或移除,会影响性能。 -
队列大小初始化方式不同
ArrayBlockingQueue 实现的队列中必须指定队列的大小
LinkedBlockingQueue 实现的队列中可以不指定队列的大小,但是默认是 Integer.MAX_VALUE
一般情况下,在处理多线程间的生产者-消费者问题是,使用这两个就可以。
PriorityBlockingQueue
它是一个支持优先级的无界队列。默认情况下元素采取自然顺序升序排列。这里可以自定义实现 compareTo() 方法来指定元素进行排序规则;或者初始化 PriorityBlockingQueue 时,指定构造参数 Comparator 来对元素进行排序。但其不能保证同优先级元素的排序。
DelayQueue
它是一个支持延时获取元素的无界阻塞队列。队列使用 PriorityBlockingQueue 来实现。 队列中的元素必须实现 Delayed 接口。创建元素是,可以指定元素到期的时间,只有在元素到期时才能从队列中取走。
SynchronousQueue
它是一个不存储元素的阻塞队列。每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。
LinkedTransferQueue
它是一个有链表结构组成的无界阻塞 TransferQueue 队列。LinkedTransferQueue 实现一个重要的接口 TransferQueue 。该接口含有5个方法,其中有3个重要的方法。分别是:
- transfer(E e):若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;如果没有消费者在等待接受数据,就会将元素插入到队列尾部,并且等待进入阻塞状态,知道有消费者线程取走该元素。
- tryTransfer(E e): 若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;若不存在,则返回 false,并且不进入队列,这是一个不阻塞的操作。
- tryTransfer(E e, long timeout, TimeUnit unit): 若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;如果没有消费者在等待接受数据,就会将元素插入到队列尾部。若在指定的超时时间内元素未被消费者线程获取,则返回 false ;若在指定的超时时间内元素被消费者线程获取,则返回 true ;
LinkedBlockingDeque
它是一个有链表结构组成的双向阻塞队列。双向队列可以从队列的两端插入和移出元素,因此在多线程同时入队时,也就减少了一半的竞争。由于时双向的,因此 LinkedBlockingDeque 多了 addFirst、 addLast、 offerFirst、offerLast、peekFirst、peekLast 等方法,其中,以 First单词结尾的方法,表示插入、获取或移除双端队列的第一个元素;以 Last单词结尾的方法,表示插入、获取或移除双端队列的最后一个元素。