动手实现阻塞队列
源头
最近一直在看多线程和并发的知识,然后也看了线程池、阻塞队列等源码,想要造个轮子,来加深对阻塞队列源码的理解。
思路
- 阻塞队列的实现思路还是利用等待/唤醒机制的思路。
- 队列的实现使用双向链表LinkedList实现,因为队列是后进先出保证公平性,所以存元素就插入到队列的尾部,而取元素就从队列的头部取出元素。
- 队列的存取元素是在多线程的环境下,所有使用重入锁ReentrantLock实现。
- 队列满了,那么就让生产者等待,唤醒消费者。
- 队列空了,那么就让消费者等待,唤醒生产者、
- 如果队列满了,一直没有消费者消费,那么生产者就会一直阻塞。
- 如果队列空了,一直没有生产者生产,那么消费者就会一直阻塞。
实现
- 测试:
public static void main(String[] args) {
SimpleBlockingQueue<String> queue = new SimpleBlockingQueue<>(5);
for (int i = 0; i < 10 ; i ++) {
final int fi = i;
Thread t1 = new Thread(() -> {
System.out.println("=================== producer i = " + fi);
try {
queue.addByBlock(String.valueOf(fi));
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
}
for (int j = 0 ; j < 1 ; j++) {
Thread t2 = new Thread(() -> {
try {
String msg = queue.getByBlock();
System.out.println("=================== consumer msg = " + msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t2.start();
}
}
- 核心实现
/**
* 自定义简易版阻塞队列
* @author sunpeiyu
* @date 2023-05-17
* @param <T>
*/
public class SimpleBlockingQueue<T> {
// 双向链表存放元素
private final LinkedList<T> mainList;
// 多线程存取队列元素的锁
private final ReentrantLock mainLock;
// 存入元素的等待队列
private final Condition produceCondition;
// 取出元素的等待队列
private final Condition consumeCondition;
// 阻塞队列的长度(双向链表的长度)
private final Integer queueSize;
// 统计阻塞队列中元素个数
private Integer count = 0;
public SimpleBlockingQueue(Integer queueSize) {
this.queueSize = queueSize;
this.mainList = new LinkedList<>();
this.mainLock = new ReentrantLock();
this.produceCondition = this.mainLock.newCondition();
this.consumeCondition = this.mainLock.newCondition();
}
public void addByBlock(T t) throws InterruptedException {
if (Objects.isNull(t)) {
throw new NullPointerException("参数t不能为空!");
}
mainLock.lock();
try {
// 队列已经满了,添加元素等待
while (Objects.equals(this.count, queueSize)) {
// 生产等待队列中的线程先等待
this.produceCondition.await();
}
// 队列未满,入队
this.mainList.addLast(t);
++this.count;
// 消费等待队列中的线程唤醒
this.consumeCondition.signal();
} finally {
mainLock.unlock();
}
}
public T getByBlock() throws InterruptedException {
mainLock.lock();
T t = null;
try {
// 队列已经满了,添加元素等待
while (this.count == 0) {
// 生产等待队列中的线程先等待
this.consumeCondition.await();
}
// 队列未空,出队
t = this.mainList.pollFirst();
--this.count;
// 消费等待队列中的线程唤醒
this.produceCondition.signal();
} finally {
mainLock.unlock();
}
return t;
}
}
参考
< JDK 1.8.0_65 源码>