a)队列为空,尝试出队列,出队列操作就会阻塞,阻塞到其他线程添加元素为止
b)队列为满,尝试入队列,入队列操作也会阻塞,阻塞到其他线程取走元素为止
下面是Java中阻塞队列接口 BlockingDeque 由其LinkedBlockingDeque类实现(双端队列)
也可以由普通阻塞队列实现BlockingQueue 由ArrayBlockingQueue 和 LinkedBlockingQueue,PriorityBlockingQueue, TransferQueue实现
下面是用的双端队列
public class Demo {
public static void main(String[] args) throws InterruptedException {
BlockingDeque queue = new LinkedBlockingDeque<>();//阻塞队列(天然线程安全)
queue.put("abcd");//放元素put 也可以用普通队列的offer 但是没有阻塞功能
//这里的阻塞是队列满的时候就在这里阻塞
String elem = queue.take();//出元素take 也可以用poll 也是没有阻塞功能
//这里的阻塞是队列为空的时候阻塞
System.out.println(elem);
}
}
阻塞演示
public class Demo {
public static void main(String[] args) throws InterruptedException {
BlockingDeque queue = new LinkedBlockingDeque<>(100);
for (int i = 0; i < 100; i++) {
queue.put("abc");
}
System.out.println("队列已经满了");
queue.put("666");//这里就会阻塞可以通过jcosole查看线程的状态(跟踪线程)
System.out.println("再次尝试put元素");
}
}
生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取.
解耦合的目的是为了让后续的修改成本低队列一般不会修改,修改A的代码也就只用考虑修改A就行不用考虑B的代码情况B也是跟队列耦合的
public class Demo {
public static void main(String[] args) {
//至少一个生产者线程,一个消费者线程
BlockingDeque queue = new LinkedBlockingDeque<>(100);//阻塞队列大小为100
Thread producer = new Thread(() -> {//生产者线程
int n = 0;
while (true) {
try {
queue.put(n);
System.out.println("生产元素 " + n);
n++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "producer");
Thread consumer = new Thread(() -> {//消费者线程
try {
while (true) {
Integer n = queue.take();
System.out.println("消费元素 " + n);
Thread.sleep(1000);//这里也是限制
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "consumer");
producer.start();
consumer.start();
}
}
* 以上生产者和消费者之间速度差不多 能达到一种平衡 基本不会阻塞 效率很高 * 这里阻塞是为了突发情况 保证安全 (防止信息量暴增) 生产消费者模型就是在尽量不阻塞(平衡生产者消费者之间的效率)的情况下达到最高效率
class MyBlockingQueue {
private String[] data = null;
//队列首
private int head = 0;
//队列尾
private int tail = 0;
//元素个数
private int size = 0;
public MyBlockingQueue (int capacity) {
data = new String[capacity];
}
public void put(String elem) throws InterruptedException {
synchronized (this) {
while (size >= data.length) {
//队列满了 阻塞
this.wait();//wait之前判断一次 唤醒也判定一次
}
data[tail] = elem;
tail++;
if (tail >= data.length) {
tail = 0;
}
//tail = (tail + 1) % data.length; //与上面四行代码逻辑相同
size++;
this.notify();//唤醒take的wait(size++队列不为空)
}
}
public String take() throws InterruptedException {
synchronized (this) {
/**
* 如果只是一个线程take 一个线程put不会出现不会出现自己唤醒自己的情况
* 多个线程take 多个线程put 确实有自己唤醒自己的风险 但是用过while循环条件避免这样的影响
* while也能在interrupt中断的情况下还能继续让其等待 不会出现size = - 1的情况
*/
while (size == 0){
//队列空了 需要阻塞
this.wait();//wait之前判断一次 唤醒也判定一次
}
String ret = data[head];
head++;
if (head >= data.length) {
head = 0;
}
//head = (head + 1) % data.length;
size--;
this.notify();//唤醒put的wait(size--队列就不满了)
return ret;
}
}
/**
* 比如有若干个线程用这个队列
* 要么所有阻塞在put 要么 take
* 不可能出现一些阻塞在put 一些阻塞在take (因为队列不可能是既满又空)
*/
}
public class Demo {
public static void main(String[] args) {
MyBlockingQueue queue = new MyBlockingQueue(100);
Thread producer = new Thread(() -> {
int n =0;
while (true) {
try {
queue.put(n + "");
System.out.println("生产元素:" + n);
//Thread.sleep(1000);
n++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread consumer = new Thread(() -> {
while (true) {
try {
String n = queue.take();
System.out.println("消费元素:" + n);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
consumer.start();
}
}