一、了解线程池
我们知道,CPU资源是有限的,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致CPU频繁切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。
另外,当线程池没有空闲线程时,新的任务请求线程资源时,我们一般有两种处理策略,第一种是非阻塞的处理方式,直接拒绝任务请求;另一种是阻塞的处理方式,将请求排队,等到有空闲线程时,去除排队的请求继续处理,那么如何处理排队的请求呢,可以用队列来存储,有两种实现方式:第一种基于链表的实现,可以实现一个支持无限排队的无界队列,但是可能导致过多的请求排队等待,请求处理的响应时间过长,所以针对敏感的系统,无限排队的线程池是不合适的;另一种是基于数组实现的有界队列,队列的大小有限,所以排队的请求超过队列的大小时,接下来的请求就会被拒绝。
二、什么是队列 ?
队列和栈一样,也是一种 ‘操作受限’ 的线性表,只支持入队 enqueue 和出队 dequeue 两个操作,入队是将数据放到队列尾部,出队从对头取出数据。简单理解:队尾进 - 队头出。
队列作为一种非常基础的数据结构,应用也非常广泛,在很多偏低层系统、框架、中间件的开发中,起到关键性作用。比如高性能队列 Disruptor、Linux 环形缓存,都用到了循环并发队列;Java concurrent 并发包中利用 ArrayBlockingQueue 来实现公平锁等。
三、顺序队列和链式队列
用数组实现的叫顺序队列啊,用链表实现的叫链式队列。
以下代码展示如何用数组实现一个顺序队列:
public class ArrayQueue {
private String[] iterms;
private int head; //队头下标
private int tail; //队尾下标
private int n; //数组大小
public ArrayQueue(int capacity) {
this.iterms = new String[capacity];
this.head = 0;
this.tail = 0;
this.n = capacity;
}
//队尾入队
public boolean enqueue(String iterm) {
if (tail == n) {//队列已满,不可以入队
return false;
}
iterms[tail] = iterm;
tail ++;
return true;
}
//队头出队
public String dequeue() {
if (head == tail) {//表示队列为空
return null;
}
String temp = iterms[head];
head ++;
return temp;
}
}
比起用数组实现一个栈,队列的数组实现稍微有点复杂,对于栈来说,我们只需要一个栈顶指针就可以了,但是队列需要两个指针,一个是head,指向队头,一个是tail指针,指向队尾。
随着不停地入队、出队操作,head和tail都会持续往后移动,当tail移动到最右边:如果head还在队头,那么表示队列已满了,入队失败;如果head不在队头,那么为了充分利用head左边的空间,这时候会触发一次数据搬移,将head到tail之间的数据,整体搬移到数组中0到tail-head的位置。
基于链表的队列实现方法
基于链表实现同样也需要两个指针,大致实现思路是:链表尾部队尾入队时,tail.next = newNode,tail = tail.next ;从链表头部队头出队时,head= head.next。
四、循环队列
在用数组实现队列的时候,tail == n时会进行数据搬移,这样入队性能就会受到影响,这时可以尝试用循环队列解决。
队满的判断条件是(tail+1)% n = head
//队尾入队
public boolean enqueue(String iterm) {
if ((tail + 1) % n == head) {//队列已满,不可以入队
return false;
}
iterms[tail] = iterm;
tail = (tail + 1) % n;
return true;
}
//队头出队
public String dequeue() {
if (head == tail) {//表示队列为空
return null;
}
String temp = iterms[head];
head = (head + 1) % n;
return temp;
}
五、阻塞队列和并发队列
阻塞队列其实就是在队列基础上增加了阻塞操作,简单来说,在队列为空时,从头取数据会被阻塞,知道队列中有数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。
线程安全的队列我们叫并发队列,最简单直接的方式就是直接在 enqueue() 和 dequeue() 方法上加锁,但是锁力度大并发度会比较低,同一时刻仅允许一个存或者取操作。这个时候可以实现一个无锁并发队列,网上有很多讨论,先不在此处详细解释。
总之,阻塞队列就是入队、出队操作可以阻塞,并发队列就是队列的操作多线程安全。
4264

被折叠的 条评论
为什么被折叠?



