什么是阻塞队列?
阻塞队列也就是支持阻塞的队列。包括阻塞添加和阻塞移除,也就是锁的wait 和 notify 操作。
当队列满时,再添加元素就会阻塞插入队列的线程,直到队列不满;
当队列为空时,就会阻塞获取元素的线程,直到队列不为空;
注意点
队列,也就是存放数据的容器或集合,是有固定最大长度的;
需要有一个计数器,记录队列长度;
队列没有元素时,执行的线程要等待;
队列元素已满时,执行队列的元素也要等待;
既然可能阻塞,肯定需要加锁解锁;
代码模拟
定义阻塞队列类MyBlockingQueue
定义队列容器、计数器、最大长度、最小长度、对象同步锁
定义带有参数的构造器、添加方法、获取第一个元素的方法、获取容器长度方法、获取容器对象的方法
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class MyBlockingQueue {
//存放数据的集合
private final LinkedList<Object> list = new LinkedList<>();
//计数器 AtomicInteger:原子性
private final AtomicInteger count = new AtomicInteger(0);
//队列的最大长度
private final int maxSize;
//队列的最小长度
private final int minSize = 0;
//对象锁
private final Object lock = new Object();
//带有参数的构造方法,生成带有最大长度的阻塞队列对象
public MyBlockingQueue(int maxSize) {
this.maxSize = maxSize;
}
/**添加元素*/
public void add(Object obj) {
//因为满足条件可能会加上锁,所以需要同步处理
synchronized (lock) {
//当容器长度已经是最大长度时,需要阻塞等待,不能进行添加操作
while(list.size() == this.maxSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当容器长度小于最大长度时添加元素
list.add(obj);
//计数器加1,相当于i++
count.getAndIncrement();
System.err.println("元素 "+ obj + " 已经添加到容器中!~");
//如果之前容器长度为最小值0,执行获取元素方法take()时,就会阻塞,
//所以此时添加元素后,容器长度不为0,阻塞的take方法需要唤醒
lock.notify();
}
}
/**获取第一个元素*/
public Object take() {
Object temp = null;
//由于队列容器可能长度为最小0,所以获取时会阻塞
synchronized (lock) {
while(list.size() == this.minSize) {
try {
lock.wait();
System.err.println("容器长度为0,不能获取到元素 ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当容器中有元素时,可以获取第一个元素
temp = list.removeFirst();
//获取第一个元素后,计数器要进行减减操作,相当于i--
count.getAndDecrement();
System.err.println("元素 "+ temp + " 已经从容器中移除!~");
//因为可能队列长度是最大值,之前的添加操作已经阻塞,所以此时需要唤醒
lock.notify();
}
return temp;
}
/**获取队列的长度*/
public int size() {
return count.get();
}
/**获取队列的容器对象*/
public List<Object> getQueueList(){
return list;
}
}
测试
public static void main(String[] args) {
MyBlockingQueue mbq = new MyBlockingQueue(5);
mbq.add("a");
mbq.add("b");
mbq.add("c");
mbq.add("d");
mbq.add("e");
System.err.println("当前元素个数为: " + mbq.size());
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
mbq.add("f");
mbq.add("g");
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
mbq.take();
Thread.sleep(1000);
mbq.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2");
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println("当前元素:" + mbq.getQueueList());
}
测试结果为:
分析:
首先添加5个元素,打印元素的个数,然后先执行线程t1,因为容器已经为最大长度,所以t1阻塞,然后执行线程t2,元素 a 从容器中移除,唤醒t1线程,元素 f 添加到容器,唤醒t2,元素 b 从容器中移除,唤醒t1线程,元素 g 添加到容器容器长度又为最大值,打印当前元素:[c, d, e, f, g]