目录
阻塞队列
阻塞队列是什么
阻塞队列满足了队列的结构和特性(链式或数组结构,满足先进先出原则),同时也满足线程安全:①入队操作:如果队列满了,就需要等待;②出队操作:如果队列为空,就需要等待.
阻塞队列的作用
①“消峰”作用
当生产过快时,消费者可能消费不过来,此时就需要阻塞队列来充当缓冲。
例如快递公司派送快递,生产操作就相当于快递公司不断接受到需要寄出的包裹,消费操作相当于是派送快递的快递员,而快递仓库就是阻塞队列了。
当某一时间同时快递公司收到了很多的订单,而目前的快递员不足以同时将这些快递送出,此时快递仓库就起到了缓冲作用,订单每次存入快递仓库,而快递员直接从仓库中取出去送即可,这就是“消峰”。
②对生产者和消费者进行解耦
解耦即双方不产生依赖关系。就如上述的例子,生产者只是将快递存入仓库,而不是直接给消费者快递员去派送;而快递员也是从仓库中取出快递去送,此时生产者与消费者之间就不存在强耦合问题了。
阻塞队列的实现代码
下述代码是以数组结构来实现的阻塞队列,满足线程安全,满足队列的结构和特性,先进先出原则.
public class BlockingQueue {
//循环数组:存取元素
private int[] elements;
//有效负荷
private int size;
//放元素的索引
private int putIndex;
//取元素的索引
private int takeIndex;
/**
* @param capacity 容量
*/
public BlockingQueue(int capacity) {
elements = new int[capacity];
}
/**
* 放元素到阻塞队列(需要保证线程安全,如果满了需要等待)
* @param element
*/
public synchronized void put(int element) throws InterruptedException {
//如果满了就等待
while(elements.length == size) {
this.wait();
}
//如果不满就放入阻塞队列
elements[putIndex] = element;
//放的索引往后移动一位(取模是在最后一个位置就往首位移动)
putIndex = (putIndex+1) % elements.length;
//有效负荷+1
size++;
//通知wait等待的线程
this.notifyAll();
}
/**
* 取元素,如果队列为空,就需要等待
* @return element
*/
public synchronized int take() throws InterruptedException {
//如果是空,就等待
while(size == 0) {
this.wait();
}
//如果不空就从阻塞队列取出
int element = elements[takeIndex];
//取的索引往后移动一位(取模是在最后一个位置就往首位移动)
takeIndex = (takeIndex+1) % elements.length;
//有效负荷+1
size--;
//通知wait等待的线程
this.notifyAll();
return element;
}
}
生产者与消费者模型
生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而是通过阻塞队列来实现通讯,所以生产者生产完数据后不需要等待消费者处理,直接存入阻塞队列,而消费者也不找生产者要数据,而是直接从阻塞队列中取出.
在生产者与消费者模型中阻塞队列相当于一个缓冲区,平衡了生产者和消费者的处理能力,阻塞队列也能使生产者与消费者之间解耦.
具体代码
下述生产者与消费模型应用的是上面所实现的阻塞队列,阻塞队列在生产者与消费者之间起到了缓冲、解耦的作用.
生产者每隔1s随机生成一个数组并将其存入阻塞队列,消费者不断从阻塞队列中取出元素。
/**
* 生产者与消费者模型
*/
class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
BlockingQueue queue = new BlockingQueue(20);
new Thread(new Runnable() {
@Override
public void run() {
//模拟生产者
while(true) {
try {
queue.put(new Random(1000).nextInt());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
//模拟消费者
while(true) {
int element = 0;
try {
element = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("取到的数字为: " + element);
}
}
}).start();
}
}
运行结果