1、双端队列(Deque)
deque 特殊之处在于添加和删除项是非限制性的。可以从任一端添加和删除数据。这种混合线性结构提供了单个数据结构中的栈和队列的所有能力。
2、循环队列
用数组来实现队列的时候,利用头尾指针避免了出队时数据的搬移,只是再在 tail==数组长度 时,会有数据搬移操作,但数据搬移操作还是会影响到性能。解决方案就是循环队列。
指定队列固定大小,通过头尾指针控制元素的增删。循环队列的关键是做好对空和对满的条件判断。
前面用数组+指针实现的单向队列中,队满的判断条件是 tail == n,队空的判断条件是 head == tail。
循环队列:队列为空的判断条件仍然是 head == tail
当队满时,(tail+1)%n=head
(tail 指向的位置实际上总是没有存储数据的)
public class CircularQueue {
/**
* 容器
*/
private Object[] elementData;
/**
* 队列的默认大小
*/
private static final int DEFAULT_SIZE = 10;
/**
* 队列中头指针
*/
private int head;
/**
* 队列中尾指针
*/
private int tail;
/**
* 默认构造器
*/
public CircularQueue() {
this(DEFAULT_SIZE);
}
public CircularQueue(int capacity) {
if (capacity < 0) {
throw new RuntimeException();
}
elementData = new String[capacity];
}
// 入队
public boolean enqueue(Object data) {
// 队列满了
if ((tail + 1) % elementData.length == head) {
return false;
}
elementData[tail] = data;
tail = (tail + 1) % elementData.length;
return true;
}
// 出队
public Object dequeue() {
// 如果 head == tail 表示队列为空
if (head == tail) {
return null;
}
Object data = elementData[head];
head = (head + 1) % elementData.length;
return data;
}
}
3、阻塞队列
队列为空的时候,从队头取数据会被阻塞。因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。
public class BlockingQueue {
private List queue = new LinkedList();
private int size;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public BlockingQueue(int size) {
this.size = size;
}
//入队列时,如果队列满了,则阻塞
public void enqueue(Object item) throws InterruptedException {
lock.lock();
try {
while (this.queue.size() == this.size) {
condition.await();
}
if (this.queue.size() == 0) {
condition.signalAll();
}
this.queue.add(item);
} finally {
lock.unlock();
}
}
//出队列时,如果队列为空,则阻塞
public Object dequeue() throws InterruptedException {
lock.lock();
try {
while (this.queue.size() == 0) {
condition.await();
}
if (this.queue.size() == this.size) {
condition.signalAll();
}
return this.queue.remove(0);
} finally {
lock.unlock();
}
}
}
队列的应用:
队列在线程池中的应用:LinkedBlockingQueue(基于链表的阻塞队列)
构建可重用固定线程数的线程池:newFixedThreadPool,FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。当线程池中的线程数大于corePoolSize时,keepAliveTime设置为0L,意味着多余 的空闲线程会被立即终止。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
执行流程:
1)如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。
2)在线程池完成预热之后(当前运行的线程数等于corePoolSize),将任务加入LinkedBlockingQueue。
3)线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行
使用无界队列作为工作队列,会对线程池带来如下影响:
- 当线程池中的线程数只要一达到corePoolSize后,新任务将添加到无界阻塞队列中。因此线程池中的线程数不会超过corePoolSize。
- 无界队列所以maximumPoolSize将是一个无效参数,那么keepAliveTime也是一个无效参数。
- 线程池不会有拒绝任务,来者不拒,均加入队列中