队列(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,一个由优先级堆支持的、基于时间的调度队列。