1,队列的概念
队列这个概念非常好理解。你可以把它想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。先进者先出,这就是典型的“队列”
我们知道,栈只支持两个操作,push(入栈)和pop(出栈),队列跟栈很像,同样也只支持两个操作,enqueue(入队)和dequeue(出队)。
2,队列的实现
队列可以用数组实现,也可以用链表实现。用数组实现的队列称为顺序队列,用链表实现的队列称为链式队列。下面简单看下两种方式的实现。
2.1 数组实现队列
public class ArrayQueue {
int capacity;
String [] queue;
int head = 0,tail = 0;
public ArrayQueue(int capacity){
queue = new String[capacity];
this.capacity = capacity;
}
public void enqueue(String value){
if(tail == capacity){
//满了,自动扩容
resize();
}
queue[tail] = value;
tail++;
}
public String dequeue(){
if(head == tail) return null;
String value = queue[head];
queue[head] = null;
head++;
return value;
}
public void resize(){
capacity *= 2;
String [] newQueue = new String[capacity];
//数据搬到一个新的数组
System.arraycopy(queue, head, newQueue, 0, queue.length - head);
tail = queue.length - head;
queue = newQueue;
head = 0;
}
}
2.2 链表实现队列
public class LinkedQueue {
/**
* 链表节点
*
*/
private class Node{
String value;
Node next;
public Node(String value){
this.value = value;
}
}
Node head;
Node tail;
public void enqueue(String value){
Node node = new Node(value);
if(head == null){
head = node;
}
if(tail != null){
tail.next = node;
}
tail = node;
}
public String dequeue(){
Node tmp = head.next;
String result = head.value;
head = tmp;
return result;
}
}
3,队列的应用
1,阻塞队列
1)在队列的基础上增加阻塞操作,就成了阻塞队列。
2)阻塞队列就是在队列为空的时候,从队头取数据会被阻塞,因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后在返回。
3)从上面的定义可以看出这就是一个 生产者 消费者模型 。这种基于阻塞队列实现的生产者消费者模型可以有效地协调生产和消费的速度。当生产者生产数据的速度过快, “ 消费者 ” 来不及消费时,存储数据的队列很快就会满了,这时生产者就阻塞等待,直到 “ 消费者 ” 消费了数据,“ 生产者 ” 才会被唤醒继续生产。不仅如此,基于阻塞队列,我们还可以通过协调 “ 生产者 ” 和 “ 消费者 ” 的个数,来提高数据处理效率,比如配置几个消费者,来应对一个生产者。
2. 并发队列
1)在多线程的情况下,会有多个线程同时操作队列,这时就会存在线程安全问题。能够有效解决线程安全问题的队列就称为并发队列。
2)并发队列简单的实现就是在 enqueue() 、 dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或取操作。
3)实际上,基于数组的循环队列利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。
3. 线程池资源枯竭时的处理
在资源有限的场景,当没有空闲资源时,基本上都可以通过 “ 队列 ” 这种数据结构来实现请求排队。