BlockingQueue介绍与常用方法
BlockingQueue是一个阻塞队列。在高并发场景是用得非常多的,在线程池中。如果运行线程数目大于核心线程数目时,也会尝试把新加入的线程放到一个BlockingQueue中去。队列的特性就是先进先出很容易理解,在java里头它的实现类主要有下图的几种,其中最常用到的是ArrayBlockingQueue、LinkedBlockingQueue及SynchronousQueue这三种。
它主要的方法有
BlockingQueue的核心方法:
1、放入数据
(1) add(object)
队列没满的话,放入成功。否则抛出异常。
(2)offer(object):
表示如果可能的话,将object加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
(3)offer(E o, long timeout, TimeUnit unit)
可以设定等待的时间,如果在指定的时间内,还不能往队列中加入BlockingQueue,则返回失败。
(4)put(object)
把object加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程阻塞。直到BlockingQueue里面有空间再继续.
2、获取数据
(1)poll(time)
取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null;
(2)poll(long timeout, TimeUnit unit)
从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
(3)take()
取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入;
(4)drainTo()
一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
ArrayBlockingQueue
一个由数组支持的有界阻塞队列。它的本质是一个基于数组的BlockingQueue的实现。
它的容纳大小是固定的。此队列按 FIFO(先进先出)原则对元素进行排序。
队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。
新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。
一旦创建了这样的缓存区,就不能再增加其容量。
试图向已满队列中放入元素会导致放入操作受阻塞,直到BlockingQueue里有新的唤空间才会被醒继续操作;
试图从空队列中检索元素将导致类似阻塞,直到BlocingkQueue进了新货才会被唤醒。
此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。
默认情况下,不保证是这种排序。然而,通过在构造函数将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。
公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。
此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。
注意1:它是有界阻塞队列。它是数组实现的,是一个典型的“有界缓存区”。数组大小在构造函数指定,而且从此以后不可改变。
注意2:是它线程安全的,是阻塞的,具体参考BlockingQueue的“注意4”。
注意3:不接受 null 元素
注意4:公平性 (fairness)可以在构造函数中指定。
Public Constructors |
|
ArrayBlockingQueue(int capacity)
Creates an ArrayBlockingQueue with the given (fixed) capacity and default access policy.
|
|
ArrayBlockingQueue(int capacity, boolean fair)
Creates an ArrayBlockingQueue with the given (fixed) capacity and the specified access policy.
|
|
ArrayBlockingQueue(int capacity, boolean fair, Collection extends E> c)
Creates an ArrayBlockingQueue with the given (fixed) capacity, the specified access policy and initially containing the elements of the given collection, added in traversal order of the collection's iterator.
|
如果为true,则按照 FIFO 顺序访问插入或移除时受阻塞线程的队列;如果为 false,则访问顺序是不确定的。
注意5:它实现了BlockingQueue接口。
注意6:此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。
注意7:其容量在构造函数中指定。容量不可以自动扩展,也没提供手动扩展的接口。
注意8:在JDK5/6中,LinkedBlockingQueue和ArrayBlocingQueue等对象的poll(long timeout, TimeUnit unit)存在内存泄露
Leak的对象是AbstractQueuedSynchronizer.Node,
据称JDK5会在Update12里Fix,JDK6会在Update2里Fix。
源码分析:
一个基本数组的阻塞队列。可以设置列队的大小。
它的基本原理实际还是数组,只不过存、取、删时都要做队列是否满或空的判断。然后加锁访问。
- package java.util.concurrent;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.AbstractQueue;
- import java.util.Collection;
- import java.util.Iterator;
- import java.util.NoSuchElementException;
- import java.lang.ref.WeakReference;
- import java.util.Spliterators;
- import java.util.Spliterator;
-
-
- public class ArrayBlockingQueue extends AbstractQueue
- implements BlockingQueue, java.io.Serializable {
-
- private static final long serialVersionUID = -817911632652898426L;
-
-
- final Object[] items;
-
-
- int takeIndex;
-
-
- int putIndex;
-
-
- int count;
-
-
-
- final ReentrantLock lock;
-
-
- private final Condition notEmpty;
-
-
- private final Condition notFull;
-
- transient Itrs itrs = null;
-
-
-
-
-
- final int dec(int i) {
- return ((i == 0) ? items.length : i) - 1;
- }
-
-
-
-
- @SuppressWarnings("unchecked")
- final E itemAt(int i) {
- return (E) items[i];
- }
-
-
-
-
-
-
- private static void checkNotNull(Object v) {
- if (v == null)
- throw new NullPointerException();
- }
-
-
-
-
-
- private void enqueue(E x) {
- final Object[] items = this.items;
- items[putIndex] = x;
- if (++putIndex == items.length)
- putIndex = 0;
- count++;
- notEmpty.signal();
- }
-
-
-
-
-
- private E dequeue() {
- final Object[] items = this.items;
- @SuppressWarnings("unchecked")
- E x = (E) items[takeIndex];
- items[takeIndex] = null;
- if (++takeIndex == items.length)
- takeIndex = 0;
- count--;/当前拥有元素个数减1
- if (itrs != null)
- itrs.elementDequeued();
- notFull.signal();
- return x;
- }
-
-
-
-
-
- void removeAt(final int removeIndex) {
- final Object[] items = this.items;
- if (removeIndex == takeIndex) {
- items[takeIndex] = null;
- if (++takeIndex == items.length)
- takeIndex = 0;
- count--;
- if (itrs != null)
- itrs.elementDequeued();
- } else {
- final int putIndex = this.putIndex;
- for (int i = removeIndex;;) {
- int next = i + 1;
- if (next == items.length)
- next = 0;
- if (next != putIndex) {
- items[i] = items[next];
- i = next;
- } else {
- items[i] = null;
- this.putIndex = i;
- break;
- }
- }
- count--;
- if (itrs != null)
- itrs.removedAt(removeIndex);
- }
- notFull.signal();
- }
-
-
-
-
-
- public ArrayBlockingQueue(int capacity) {
- this(capacity, false);
- }
-
-
-
-
-
- public ArrayBlockingQueue(int capacity, boolean fair) {
- if (capacity <= 0)
- throw new IllegalArgumentException();
- this.items = new Object[capacity];
- lock = new ReentrantLock(fair);
-
- notEmpty = lock.newCondition();
- notFull = lock.newCondition();
- }
-
-
-
-
- public ArrayBlockingQueue(int capacity, boolean fair,
- Collection extends E> c) {
- this(capacity, fair);
-
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int i = 0;
- try {
- for (E e : c) {
- checkNotNull(e);
- items[i++] = e;
- }
- } catch (ArrayIndexOutOfBoundsException ex) {
- throw new IllegalArgumentException();
- }
- count = i;
- putIndex = (i == capacity) ? 0 : i;
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
- public boolean add(E e) {
- return super.add(e);
- }
-
-
-
-
-
- public boolean offer(E e) {
- checkNotNull(e);
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (count == items.length)
- return false;
- else {
- enqueue(e);
- return true;
- }
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
- public void put(E e) throws InterruptedException {
- checkNotNull(e);
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- while (count == items.length)
- notFull.await();
- enqueue(e);
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
- public boolean offer(E e, long timeout, TimeUnit unit)
- throws InterruptedException {
-
- checkNotNull(e);
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- while (count == items.length) {
- if (nanos <= 0)
- return false;
- nanos = notFull.awaitNanos(nanos);
- }
- enqueue(e);
- return true;
- } finally {
- lock.unlock();
- }
- }
-
-
- public E poll() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return (count == 0) ? null : dequeue();
- } finally {
- lock.unlock();
- }
- }
-
- public E take() throws InterruptedException {
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- while (count == 0)
- notEmpty.await();
- return dequeue();
- } finally {
- lock.unlock();
- }
- }
-
- public E poll(long timeout, TimeUnit unit) throws InterruptedException {
- long nanos = unit.toNanos(timeout);
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- while (count == 0) {
- if (nanos <= 0)
- return null;
- nanos = notEmpty.awaitNanos(nanos);
- }
- return dequeue();
- } finally {
- lock.unlock();
- }
- }
-
- public E peek() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return itemAt(takeIndex);
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
-
- public int size() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return count;
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
- public int remainingCapacity() {
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- return items.length - count;
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
- public boolean remove(Object o) {
- if (o == null) return false;
- final Object[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (count > 0) {
- final int putIndex = this.putIndex;
- int i = takeIndex;
- do {
- if (o.equals(items[i])) {
- removeAt(i);
- return true;
- }
- if (++i == items.length)
- i = 0;
- } while (i != putIndex);
- }
- return false;
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
- public boolean contains(Object o) {
- if (o == null) return false;
- final Object[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- if (count > 0) {
- final int putIndex = this.putIndex;
- int i = takeIndex;
- do {
- if (o.equals(items[i]))
- return true;
- if (++i == items.length)
- i = 0;
- } while (i != putIndex);
- }
- return false;
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
-
- public void clear() {
- final Object[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int k = count;
- if (k > 0) {
- final int putIndex = this.putIndex;
- int i = takeIndex;
- do {
- items[i] = null;
- if (++i == items.length)
- i = 0;
- } while (i != putIndex);
- takeIndex = putIndex;
- count = 0;
- if (itrs != null)
- itrs.queueIsEmpty();
- for (; k > 0 && lock.hasWaiters(notFull); k--)
- notFull.signal();
- }
- } finally {
- lock.unlock();
- }
- }
-
-
-
-
- public int drainTo(Collection super E> c) {
- return drainTo(c, Integer.MAX_VALUE);
- }
-
-
-
-
- public int drainTo(Collection super E> c, int maxElements) {
- checkNotNull(c);
- if (c == this)
- throw new IllegalArgumentException();
- if (maxElements <= 0)
- return 0;
- final Object[] items = this.items;
- final ReentrantLock lock = this.lock;
- lock.lock();
- try {
- int n = Math.min(maxElements, count);
- int take = takeIndex;
- int i = 0;
- try {
- while (i < n) {
- @SuppressWarnings("unchecked")
- E x = (E) items[take];
- c.add(x);
- items[take] = null;
- if (++take == items.length)
- take = 0;
- i++;
- }
- return n;
- } finally {
-
- if (i > 0) {
- count -= i;
- takeIndex = take;
- if (itrs != null) {
- if (count == 0)
- itrs.queueIsEmpty();
- else if (i > take)
- itrs.takeIndexWrapped();
- }
- for (; i > 0 && lock.hasWaiters(notFull); i--)
- notFull.signal();
- }
- }
- } finally {
- lock.unlock();
- }
- }
-
-
- }
使用实例:
生产者-消费者模型
大量的实现
ArrayBlockingQueue已经做掉了,包括判空,线程挂起等操作都封装在
ArrayBlockingQueue中。生产者只需要关心生产,消费者只需要关心消费。而如果不使用ArrayBlockingQueue的话,具体的生产者还需要去通知消费者,还需要关心整个容器是否满了。从这里可以看出
ArrayBlockingQueue是一种比较好的实现方式,高度的内聚。
Producer.java
-
- public class Producer implements Runnable{
-
-
- private final ArrayBlockingQueue queue;
-
- public Producer(ArrayBlockingQueue queue){
- this.queue = queue;
- }
-
-
-
-
- @Override
- public void run() {
- while(true){
- produce();
- }
- }
-
- public void produce(){
-
-
-
-
- try {
- Bread bread = new Bread();
- queue.put(bread);
- System.out.println("Producer:"+bread);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
Consumer.java
-
- public class Consumer implements Runnable{
-
-
- private final ArrayBlockingQueue queue;
-
- public Consumer(ArrayBlockingQueue queue){
- this.queue = queue;
- }
-
-
-
-
- @Override
- public void run() {
- while(true){
- consume();
- }
- }
-
- public void consume(){
-
-
-
-
- try {
- Bread bread = queue.take();
- System.out.println("consumer:"+bread);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
Client.java
-
- public class Client {
-
-
-
-
- public static void main(String[] args) {
- int capacity = 10;
- ArrayBlockingQueue queue = new ArrayBlockingQueue(capacity);
-
- new Thread(new Producer(queue)).start();
- new Thread(new Producer(queue)).start();
- new Thread(new Consumer(queue)).start();
- new Thread(new Consumer(queue)).start();
- new Thread(new Consumer(queue)).start();
- }
-
- }
参考:http://blog.csdn.net/evankaka/article/details/51706109
http://blog.csdn.net/hudashi/article/details/7076745