java提供的线程安全的Queue可以分为阻塞队列与非阻塞队列。其中阻塞队列的典型代表就是LinkedBlockingQueue与ArrayBlockingQueue,非阻塞队列的代表就是ConcurrentLinkedQueue。下面对阻塞个队列进行总结一下。
LinkedBlockingQueue创建的时候可以指定队列大小,也可以不指定,不指定默认是Integer.MAX_VALUE。看源码如下:
public LinkedBlockingQueue() { this(Integer.MAX_VALUE); } /** * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity. * * @param capacity the capacity of this queue * @throws IllegalArgumentException if {@code capacity} is not greater * than zero */ public LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; last = head = new Node<E>(null); }
LinkedBlockingQueue添加元素方法有add(),offer(),put()。下面谈谈这三个方法的区别,add()方法如下:
public static void main(String[]args){ LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(2); queue.add("A"); queue.add("B"); queue.add("C"); }
输出结果如下,也就是add方法当元素的个数超出了队列的大小,会抛出异常。
offer()方法如下:
public static void main(String[]args){ LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(2); Boolean q1=queue.offer("A"); Boolean q2=queue.offer("B"); Boolean q3=queue.offer("C"); System.out.println(queue.toString()); System.out.println(q1); System.out.println(q2); System.out.println(q3); }
输出结果如下:也就是说offer()方法当元素的个数超出队列大小的时候,不会抛出异常,而是添加失败,返回false。
put()方法如下:
public static void main(String[]args) throws InterruptedException{ LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(2); queue.put("A"); queue.put("B"); queue.put("C"); System.out.println("队列进入阻塞状态"); }
输出结果如下:队列进入阻塞状态,直到队列中有元素被消费,元素C才能添加到队列中。
同样,LinkedBlockingQueue移除元素也有三个方法,区别如下:
poll()方法:当队列为空的时候,返回null。
remove()方法:当队列为空的时候,抛出异常。
take()方法:当队列为空的时候,进入阻塞状态。
注意peek()方法,虽然也是取出头元素,但是不删除元素。
接下看一下ArrayBlockingQueue,同样也是阻塞队列,但是队列是有界队列,也就是说创建ArrayBlockingQueue队列的时候,必须指定队列的大小。源码如下:capacity指定队列的大小,fair指定锁是公平锁还是非公平锁。
public ArrayBlockingQueue(int capacity) { this(capacity, false); } /** * Creates an {@code ArrayBlockingQueue} with the given (fixed) * capacity and the specified access policy. * * @param capacity the capacity of this queue * @param fair if {@code true} then queue accesses for threads blocked * on insertion or removal, are processed in FIFO order; * if {@code false} the access order is unspecified. * @throws IllegalArgumentException if {@code capacity < 1} */ 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(); }
同样,添加元素,移触元素的方法与LinkedBlockingQueue的一样。
接下来总结一下这两个阻塞队列的不同之处:
(1)队列中锁的实现不同
ArrayBlockingQueue实现的队列中的锁是没有分离的,即生产和消费用的是同一个锁。
LinkedBlockingQueue实现的队列中的锁是分离的,即生产用的是putLock,消费是takeLock。
所以在多线程情况下,由于LinkedBlockingQueue的读写锁分离,所以效率会高于ArrayBlockingQueue。
(2)队列大小初始化方式不同
ArrayBlockingQueue实现的队列中必须指定队列的大小。
LinkedBlockingQueue实现的队列中可以不指定队列的大小,但是默认是Integer.MAX_VALUE。
(3)在生产或消费时操作不同
ArrayBlockingQueue实现的队列中在生产和消费的时候,是直接将枚举对象插入或移除的。
LinkedBlockingQueue实现的队列中在生产和消费的时候,需要把枚举对象转换为Node<E>进行插入或移除,会影响性能。
对队列进行如下测试:分别将1000000元素推入到两个队列中,分别计算一下耗时时间。
public static void main(String[]args) throws InterruptedException{ ArrayBlockingQueue<Integer> queue=new ArrayBlockingQueue<Integer>(1000000); long start = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ queue.add(i); } long end = System.currentTimeMillis(); System.out.println("ArrayBlockingQueue进行10000次插入操作耗时:"+(end-start)+"ms"); LinkedBlockingQueue<Integer> queue1=new LinkedBlockingQueue<Integer>(1000000); start = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ queue1.add(i); } end = System.currentTimeMillis(); System.out.println("LinkedBlockingQueue进行10000次插入操作耗时:"+(end-start)+"ms"); }
输出结果:LinkedBlockingQueue的性能比ArrayBlockingQueue要差很多。
参考网址:http://super-robin.iteye.com/blog/1997423