一.阻塞队列
1.阻塞队列是一种特殊的队列,带有阻塞性质,也遵循"先进先出",
a.针对一个已经满了的队列进行入队列,此时入队列操作就会阻塞,一直阻塞到队列不满之后
b.针对一个已经空了的队列进行出队列,此时出队列操作就会阻塞,一直阻塞到队列不空之后
2.阻塞队列是线程安全的
二.生产者消费者模型
生产者消费者模型:通过一个容器(阻塞队列),来解决生产者消费者之间的强耦合问题。(生产者生产商品,消费者消费商品)
生产者和消费者彼此之间不直接通信,而通过阻塞队列来进行通信,所以 生产者生产完数据之后,等消费者处理,直接扔给阻塞队列,消费者不找生产者数据,而是直接从阻塞队列中取
1.阻塞队列就像一个缓冲区,平衡了生产者和消费者的处理能力
外界的请求一旦突增,服务器A 的请求就会增加,压力也会大很多,服务器B和服务器C要处理的请求也会增加
完成的工作越复杂,消耗的硬件资源也会增加,
一旦外界的请求出现突发峰值,突发峰值就会使服务器挂掉
引入阻塞队列之后,即使外界请求出现峰值,由队列来承担峰值请求
2.阻塞队列也能使生产者和消费者之间解耦合
三.标准库的阻塞队列
BlockingQueue 是一个接口,真正实现的类时 LinkedBlockingQueue。
还有ArrayBlockingQueue还有PriorityBlockingQueue 不常用
put:阻塞式出队列 put()方法会抛出 InterruptedException 异常,因为该方法可能会带来阻塞。而一旦阻塞了,就有可能被interrupt方法提前唤醒,此时就会抛出该异常(会带来阻塞的方法往往会抛出 InterruptedException 异常)。如果队列已满,尝试put(),就会阻塞等待,直到队列不满为止。
take:阻塞式出队列 如果队列空,尝试take(),也会阻塞等待,直到队列不空为止。
(也有 offer、poll、peek等普通队列有的方法,但是这些方法不带有阻塞特性)
public class ThreadDemo12 {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new LinkedBlockingQueue<>(100) ;
queue.put("aaa");
String elem = queue.take();
System.out.println("elem = "+ elem);
String elem1 = queue.take();
System.out.println("elem1 = "+ elem1);
}
}
四.自己实现一个阻塞队列
1)先实现普通队列
class MyBlockingQueue { private String[] elems = null; private int head = 0; private int tail = 0; private int size = 0; private Object locker = new Object(); public MyBlockingQueue (int capacity) { elems = new String[capacity]; } public void put(String elem){ if(size >= elems.length){ return; } elems[tail] =elem; tail++; if(tail >= elems.length) { tail = 0; } size++; } public String take () { String elem = null; if(size == 0) { } elem = elems[head]; head++; if(head >= elems.length) { head = 0; } size--; return elem; } } public class ThreadDemo13 { public static void main(String[] args) { MyBlockingQueue queue = new MyBlockingQueue(100); queue.put("aaa"); queue.put("bbb"); queue.put("ccc"); queue.put("ddd"); String elem = ""; elem = queue.take(); System.out.println("elem =" + elem); elem = queue.take(); System.out.println("elem =" + elem); elem = queue.take(); System.out.println("elem =" + elem); elem = queue.take(); System.out.println("elem =" + elem); } }
2)再加线程安全
public void put(String elem){
synchronized (locker) {
if (size >= elems.length) {
return;
}
elems[tail] = elem;
tail++;
if (tail >= elems.length) {
tail = 0;
}
size++;
}
}
public String take () {
String elem = null;
synchronized (locker) {
if (size == 0) {
}
elem = elems[head];
head++;
if (head >= elems.length) {
head = 0;
}
size--;
}
return elem;
}
3)再加阻塞功能
public void put(String elem) throws InterruptedException {
synchronized (locker) {
if (size >= elems.length) {
locker.wait();
}
elems[tail] = elem;
tail++;
if (tail >= elems.length) {
tail = 0;
}
size++;
//入队成功之后唤醒
locker.notify();
}
}
public String take () throws InterruptedException {
String elem = null;
synchronized (locker) {
if (size == 0) {
locker.wait();
}
elem = elems[head];
head++;
if (head >= elems.length) {
head = 0;
}
size--;
//元素出队列成功之后,唤醒
locker.notify();
}
return elem;
}
假如两个线程都执行到了put
源代码
class MyBlockingQueue {
private String[] elems = null;
private int head = 0;
private int tail = 0;
private int size = 0;
private Object locker = new Object();
public MyBlockingQueue (int capacity) {
elems = new String[capacity];
}
public void put(String elem) throws InterruptedException {
synchronized (locker) {
while (size >= elems.length) {
locker.wait();
}
elems[tail] = elem;
tail++;
if (tail >= elems.length) {
tail = 0;
}
size++;
//入队成功之后唤醒
locker.notify();
}
}
public String take () throws InterruptedException {
String elem = null;
synchronized (locker) {
while (size == 0) {
locker.wait();
}
elem = elems[head];
head++;
if (head >= elems.length) {
head = 0;
}
size--;
//元素出队列成功之后,唤醒
locker.notify();
}
return elem;
}
}
public class ThreadDemo13 {
public static void main(String[] args) {
MyBlockingQueue queue = new MyBlockingQueue(100);
//生产者
Thread t1 = new Thread(()->{
int n = 1;
while (true) {
try {
queue.put(n + "");
System.out.println("生产元素" + n);
n++;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
//消费者
Thread t2 = new Thread(()->{
while(true) {
String n = null;
try {
n = queue.take();
System.out.println("消费元素" + n);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
t2.start();
}
}