深入理解Java集合框架系列-第四章java中的队列
一、队列的基本概念
队列是一种数据结构,它有两个基本操作:在队列尾部加人一个元素,在队列头部移除一个元素,在队列这种数据结构中,最先插入的元素将是最先被删除的元素;反之最后插入的元素将是最后被删除的元素,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。
在Java5中新增加了java.util.Queue接口,用以支持队列的常见操作。Queue接口与List、Set同一级别,都是继承了Collection接口。
上图是一个有6个存储空间的顺序队列的动态示意图,图中front指示队头,rear指示队尾。
二、Queue接口中定义的方法
1、add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
2、remove 移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
3、element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常
4、offer 添加一个元素并返回true 如果队列已满,则返回false
5、poll 移除并返问队列头部的元素 如果队列为空,则返回null
6、peek 返回队列头部的元素 如果队列为空,则返回null
Queue尽量避免使用从Collection的继承下来的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常。 如果只是取出而不移出该元素,使用element()或者peek()方法。
三、Queue接口的实现类
值得注意的是LinkedList类及其子类实现了Queue接口,因此我们可以把
LinkedList当成Queue来用。LinkedList实现了Queue接口。Queue接口窄化了对LinkedList的方法的访问权限(即在方法中的参数类型如果是Queue时,就完全只能访问Queue接口所定义的方法 了,而不能直接访问 LinkedList的非Queue的方法)。
package cn.com.queue;
import java.util.LinkedList;
import java.util.Queue;
public class TestQueue {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<Integer>();
queue.offer(1);
queue.offer(1);
queue.offer(0);
System.out.println(queue.size());
Integer i;
while((i=queue.poll())!=null){
System.out.print(i);
}
System.out.println();
System.out.println(queue.size());
}
}
四、Queue接口的4个变种
java.ulil.concurrent包提供了阻塞队列的4个变种:
1、LinkedBlockingQueue的容量是没有上限的(在不指定时容量为Integer.MAX_VALUE),但是也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。
2、ArrayBlockingQueue在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理,通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序。
3、PriorityBlockingQueue是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除。
4、DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。
五、队列的应用
java 5之后,可以使用组阻塞队列来实现线程同步,此方式大大简少了代码量,使得多线程编程更加容易,安全方面也有保障。如果你试图向一个已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线线程把中间结果取出并在将来修改它们。队列会自动平衡负载。如果第一个线程集运行得比第二个慢,则第二个 线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来。
BlockingQueue接口是Queue的子接口,它的主要用途并不是作为容器,而是作为线程同步的的工具,因此他具有一个很明显的特性,当生产者线程试图向BlockingQueue放入元素时,如果队列已满,则线程被阻塞,当消费者线程试图从中取出一个元素时,如果队列为空,则该线程会被阻塞,正是因为它所具有这个特性,所以在程序中多个线程交替向BlockingQueue中放入元素,取出元素,它可以很好的控制线程之间的通信。
package cn.com.bochy.queue;
import java.util.concurrent.BlockingQueue;
/***
* 定义一个消费者的类
* **/
public class ConsumerDemo extends Thread {
/***
* 利用队列存储产品
* */
private BlockingQueue<String> bq;
public ConsumerDemo() {
// TODO Auto-generated constructor stub
}
public ConsumerDemo(BlockingQueue<String> bq,String name) {
super();
this.bq = bq;
this.tname=name;
}
private String tname;
public String getTname() {
return tname;
}
@Override
public void run() {
while(true){
System.out.println(getTname()+"消费者准备消费集合元素");
try{
ConsumerDemo.sleep(2000);
//尝试取出元素,如果队列为空,则被线程阻塞
System.out.println(getTname()+"取出元素:"+bq.take());
}catch(Exception e){
e.printStackTrace();
}
System.out.println(getTname()+"消费完成,队列留下"+bq);
}
}
}
package cn.com.bochy.queue;
import java.util.concurrent.BlockingQueue;
/**
* 定义一个生产者
*
* */
public class ProducerDemo extends Thread{
/***
* 利用队列存储产品
* */
private BlockingQueue<String> bq;
public ProducerDemo() {
// TODO Auto-generated constructor stub
}
public ProducerDemo(BlockingQueue<String> bq,String name) {
this.bq = bq;
this.tname=name;
}
private String tname;
public String getTname() {
return tname;
}
@Override
public void run() {
String []str=new String[]{"BlockingQueue","DelayQueue","PriorityBlockingQueue"};
for(int i=0;i<100000000;i++){
System.out.println(getTname()+"生产者准备生产集合元素了!");
try{
ProducerDemo.sleep(2000);
//尝试放入元素,如果对列已满,则线程被阻塞
String input=str[i%3];
System.out.println(getTname()+"放入元素:"+input);
bq.put(input);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(getTname()+"生产完成,队列数据现为:"+bq);
}
}
}
package cn.com.bochy.queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Test {
public static void main(String[] args) {
BlockingQueue<String> bq=new LinkedBlockingQueue<String>();
try {
bq.put("12");
bq.put("13");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ConsumerDemo c=new ConsumerDemo(bq,"consumer");
ProducerDemo p=new ProducerDemo(bq,"productor");
p.start();
c.start();
}
}
更深入的内容,推荐参考:http://blog.****.net/lzy_lizhiyang/article/details/48311925