队列(Queue)是java中一种常用的数据结构,特别是在java多线程应用中,队列的使用频率很高,常常用来处理线程间数据共享的问题,今天我们来探讨一下队列的使用。

一、队列的定义

先来看看队列在java中的定义,

public interface Queue<E> extends Collection<E> {
   
    boolean add(E e);

    boolean offer(E e);

    E remove();

    E poll();

    E element();

    E peek();
}

        由定义可以发现,Queue接口跟List、Set接口一样,也继承自Collection接口,所以队列也是集合框架的一部分,具有集合的基本功能属性。那么同其他集合接口相比,队列又有哪些特殊属性呢?


二、队列的特性

        队列是一种特殊的线性表,它采用了一种被称为“先进先出”(FIFO)的存储结构,使得数据元素只能从队尾进入,从队首取出,即最先插入的元素将是最先被删除的元素,最后插入的元素将是最后被删除的元素。根据队列的这些特性,我们来看看其定义的方法。

方法名     

返回值

描述

add(E e)

boolean

将指定的元素插入此队列(如果立即可行且不会违反容量限制),在成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException。

offer(E e)

boolean

将指定的元素插入此队列(如果立即可行且不会违反容量限制),则返回 true,否则返回 false。

element()

E

获取但不移除此队列的头,如果此队列为空,则抛出NoSuchElementException。

peek()

E

获取但不移除此队列的头,如果此队列为空,则返回 null。

poll()

E

获取并移除此队列的头,如果此队列为空,则返回 null。

remove()

E

获取并移除此队列的头,如果此队列为空,则 抛出NoSuchElementException。


三、队列的实现

java中可以通过很多种方法来实现队列,我们来看一下几种常用的实现方法。

(1)通过数组实现

public class ArrayQueue<E> {

	private Object lock = new Object();

	// 队列大小
	private int size = 5;

	// 队列
	private E[] arrStr = (E[]) new Object[size];

	// 队列指针
	private int head = 0;

	// 队列尾指针
	private int tail = 0;

	/**
	 * 入队
	 * 
	 * @param o
	 */
	public void enqueue(E o) {
		synchronized (lock) {

			// 如果队列已满
			while ((tail + 1) % size == head) {
				try {
					System.out.println("队列已满,"
							+ Thread.currentThread().getName() + " 线程阻塞...");
					// 队列满时线程阻塞
					lock.wait();// 注,这里一定要放在while条件里,因为获取锁后,条件不一定还成立
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			// 如果队列未满
			arrStr[tail] = o;
			// 指针下移
			tail = (tail + 1) % size;
			// 入队后通知消费线程
			lock.notifyAll();
		}
	}

	/**
	 * 出队
	 * 
	 * @return
	 */
	public E dequeue() {
		synchronized (lock) {
			// 如果队列为空
			while (head == tail) {
				try {
					System.out.println("队列为空,"
							+ Thread.currentThread().getName() + " 线程阻塞...");
					// 队列空时线程阻塞
					lock.wait();// 注,这里一定要放在while条件里,因为获取锁后,条件不一定还成立
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			// 队列非空
			E tempStr = arrStr[head];

			arrStr[head] = null;// 注,这里出队后释放对象,加快回收,不然大的对象可能造内存泄露
			head = (head + 1) % size;

			// 出队后通知生产者
			lock.notifyAll();
			return tempStr;

		}
	}

	// 取队列第一个
	public E front() {
		synchronized (lock) {
			// 如果队列为空
			while (head == tail) {
				try {
					System.out.println("队列为空,"
							+ Thread.currentThread().getName() + " 线程阻塞...");
					// 队列空时线程阻塞
					lock.wait();// 注,这里一定要放在while条件里,因为获取锁后,条件不一定还成立
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			// 队列非空
			return arrStr[head];
		}
	}

	// 队列是否为空
	public boolean isEmpty() {
		return head == tail;
	}

	// 队列大小
	public int size() {

		return Math.abs(tail - head);
	}
	
	//显示队列信息
	public void showQueue(){
		for (int i = 0; i < arrStr.length; i++) {
			System.out.print(arrStr[i]==null?"":arrStr[i]);
		}
		System.out.println();
	}
}

测试类

public static void main(String[] args) {
	ArrayQueue que=new ArrayQueue();
	que.enqueue(1);
	que.enqueue(2);
	que.enqueue(3);
	que.enqueue(4);
	que.showQueue();
	System.out.println(que.front());
	que.dequeue();
	que.dequeue();
	System.out.println(que.front());
	que.showQueue();
	que.enqueue(5);
	que.enqueue(6);
	que.enqueue(7);
}

结果集

1234
1
3
34
队列已满,main 线程阻塞...

这种方式实现了队列的基本功能,还实现了多线程情况下的同步问题。


(2)通过LinkedList实现

public class LinkedListQueue {
	private LinkedList<E> linkedList = new LinkedList<E>();

	// 入队
	public void enqueue(E e) {
		linkedList.addLast(e);
	}

	// 出队
	public E dequeue() {
		return linkedList.removeFirst();
	}

	// 取队列第一个
	public E front() {
		return linkedList.getFirst();
	}

	// 队列是否为空
	public boolean isEmpty() {
		return linkedList.isEmpty();
	}

	// 队列大小
	public int size() {
		return linkedList.size();
	}
}

这种方式较为简单,因为LinkedList实现了Deque接口,所以LinkedList一般可以当做Queue来使用。当然还有一些其他实现方式,比如

循环双向链等,这里就比一一列举了。


四、常用的java队列

常用的java队列大致可以分为两种,一种实现了BlockingQueue接口,一种未实现BlockingQueue接口。
(1)未实现BlockingQueue接口的队列,主要包含LinkedList、PriorityQueue、ConcurrentLinkedQueue等。
(2)实现了BlockingQueue接口的队列,也称之为阻塞队列,主要有ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue等。

LinkedList,实现Queue接口,基于链表实现队列。

PriorityQueue,实质上维护了一个有序列表,加入到 Queue 中的元素根据它们的天然排序(通过其 java.util.Comparable 实现)或者根据传递给构造函数的 java.util.Comparator 实现来定位。

ConcurrentLinkedQueue,基于链接节点的、线程安全的队列。

ArrayBlockingQueue,一个由数组支持的有界队列。

LinkedBlockingQueue,一个由链接节点支持的可选有界队列。

PriorityBlockingQueue,实质上维护了一个有序列表,加入到 Queue 中的元素根据它们的天然排序(通过其 java.util.Comparable 实现)或者根据传递给构造函数的 java.util.Comparator 实现来定位。

DelayQueue,一个由优先级堆支持的、基于时间的调度队列。