队列(queue):
先进先出。
队列的2个基本操作:
1,入队:enqueue,放一个元素到队列尾部。
2,出队:dequeue,从队列头部取一个元素。
实现队列的2种方式:
1,顺序队列:用数组实现
private String[] data;//存数据的数组
private int size;//容量
private int head = 0;//头部位置
private int tail = 0;//尾部诶之
public ArrayQueue(int capacity) {
data = new String[capacity];
size = capacity;
}
public Boolean enqueue(String param) {//入队操作优化,可以当队列满了在执行数据移动。
if (tail == size) {
if (head == 0) {
return false;
} else {
for (int i = 0; i < tail - head; i++) {//数组满了,移动有效数据,清理无效数据。
data[i] = data[head + i];
data[head + i] = null;
}
tail = tail - head;
head = 0;
}
}
data[tail] = param;
tail++;
return true;
}
public String dequeue() {
if (data[head] == null) { return null; } String ret = data[head]; data[head] = null; head++; return ret; }
2,链式队列:用链表实现(后续实现)。
环形队列:
private String[] data;
private int size;
private int head = 0;
private int tail = 0;
public CircleQueue(int capacity) {
data = new String[capacity];
size = capacity;
}
public Boolean enqueue(String param) {
if (tail != 0 && tail%size == head) {//数据不为空并且尾部+1取余等于头部时,数据满了
return false;
}
if (tail == size) {//尾部等于最大容量时,环形结构,从0开始
tail = 0;
}
data[tail] = param;
tail++;
return true;
}
public String dequeue() {
if (data[head]== null) {
return null;
}
String ret = data[head];
data[head] = null;
head++;
head %= size;防止头部位置大于数组容量
return ret;
}
阻塞队列:
当队列头部为空时,消费数据时阻塞。当数据满了的时候,生产数据时被阻塞。
并发队列:
线程安全的队列我们叫作并发队列。
线程安全的队列我们叫作并发队列。最简单直接的实现方式是直接在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取操作。实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。
应用:
如何利用队列实现固定线程池连接数?
没有空闲线程时,处理方式:
非阻塞方式:直接拒绝。
阻塞方式:1,链表实现支持无限排队的无界队列。
2,数组实现有限大小的有界队列
详解:
线程池没有空闲线程时,新的任务请求线程资源时,线程池该如何处理?各种处理策略又是如何实现的呢?
我们一般有两种处理策略。第一种是非阻塞的处理方式,直接拒绝任务请求;另一种是阻塞的处理方式,将请求排队,等到有空闲线程时,取出排队的请求继续处理。那如何存储排队的请求呢?
我们希望公平地处理每个排队的请求,先进者先服务,所以队列这种数据结构很适合来存储排队请求。我们前面说过,队列有基于链表和基于数组这两种实现方式。这两种实现方式对于排队请求又有什么区别呢?
基于链表的实现方式,可以实现一个支持无限排队的无界队列(unbounded queue),但是可能会导致过多的请求排队等待,请求处理的响应时间过长。所以,针对响应时间比较敏感的系统,基于链表实现的无限排队的线程池是不合适的。
而基于数组实现的有界队列(bounded queue),队列的大小有限,所以线程池中排队的请求超过队列大小时,接下来的请求就会被拒绝,这种方式对响应时间敏感的系统来说,就相对更加合理。不过,设置一个合理的队列大小,也是非常有讲究的。队列太大导致等待的请求太多,队列太小会导致无法充分利用系统资源、发挥最大性能。
除了前面讲到队列应用在线程池请求排队的场景之外,队列可以应用在任何有限资源池中,用于排队请求,比如数据库连接池等。实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。