写在前面:前段时间面试的时候,碰到一个面试官针对阻塞队列问了以下几个问题,回答的很不好,感觉是自己的薄弱点,现在整理一下这些知识,如有错误还请各位大佬指正!(写出面试时问的这几个问题)
1. 阻塞队列和非阻塞队列的区别?
2. Java中的线程池参数为什么使用阻塞队列?
3. 手写一个阻塞队列的实现
1、阻塞队列和非阻塞队列
1.1、简介
阻塞队列是一种特殊的队列,支持在队列为空或满时的阻塞操作。相比之下,非阻塞队列则不会阻塞线程,而是通过返回 null 或抛出异常来表示队列为空或满。
1.2、阻塞队列的特点
阻塞队列是一个线程安全的队列,它支持以下特性:
支持阻塞插入操作:当队列已满时,插入操作将被阻塞,直到队列有空闲空间。
支持阻塞删除操作:当队列为空时,删除操作将被阻塞,直到队列中有元素可供删除。
Java 提供了多种实现阻塞队列的类,包括:
ArrayBlockingQueue
:基于数组的有界阻塞队列。LinkedBlockingQueue
:基于链表的可选有界阻塞队列。PriorityBlockingQueue
:具有优先级的无界阻塞队列。SynchronousQueue
:不存储元素的阻塞队列。
接下来,我们将以 ArrayBlockingQueue 为例,介绍如何使用阻塞队列。
1.2.1、创建阻塞队列
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(10);
上述代码创建了一个容量为 10 的 ArrayBlockingQueue 对象。我们可以将其看作是一个装有整数的队列,初始时队列为空。
1.2.2、插入元素
blockingQueue.put(1);
上述代码使用 put 方法向队列中插入元素 1。如果队列已满,则当前线程将被阻塞,直到队列有空闲空间。
1.2.3、删除元素
int element = blockingQueue.take();
上述代码使用 take 方法从队列中删除一个元素,并返回被删除的元素。如果队列为空,则当前线程将被阻塞,直到队列中有元素可供删除。
1.2.4 常见方法
1.3 非阻塞队列的特点
非阻塞队列是一个线程安全的队列,它不会阻塞线程,而是通过返回 null 或抛出异常来表示队列为空或满。
Java 提供了多种实现非阻塞队列的类,包括:
ConcurrentLinkedQueue
:基于链表的无界非阻塞队列。LinkedTransferQueue
:基于链表的无界非阻塞队列。LinkedBlockingDeque
:基于链表的可选有界非阻塞队列。
下面以 ConcurrentLinkedQueue 为例,介绍如何使用非阻塞队列。
1.3.1 创建非阻塞队列
import java.util.concurrent.ConcurrentLinkedQueue;
ConcurrentLinkedQueue<Integer> nonBlockingQueue = new ConcurrentLinkedQueue<>();
上述代码创建了一个 ConcurrentLinkedQueue 对象。我们可以将其看作是一个装有整数的队列,初始时队列为空。
1.3.2 插入元素
nonBlockingQueue.offer(1);
上述代码使用 offer 方法向队列中插入元素 1。如果队列已满,offer 方法将返回 false。
1.3.3 删除元素
Integer element = nonBlockingQueue.poll();
上述代码使用 poll 方法从队列中删除一个元素,并返回被删除的元素。如果队列为空,poll 方法将返回 null。
1.4 总结
下表展示了阻塞队列和非阻塞队列的主要区别:
2、Java线程池的参数为什么使用阻塞队列?
网上找了很多解释,我觉得下面这些解释比较让我信服(线程池相当于消费者,队列相当于是生产者,对应于生产者-消费者模型)
- 作用一:当一般队列中的任务满了后,阻塞队列可以保留之后的任务,充当一个缓冲区的作用;
- 作用二:当队列没有任务时阻塞获取任务的线程、使其进入wait状态,释放CPU资源。阻塞队列自带阻塞和唤醒功能,不需要额外的处理,无任务时线程池利用阻塞队列的take方法挂起,从而保证核心线程的存货,不至于一直占用CPU资源。
3、手写实现阻塞队列
public class BlockingQueue1 {
private int size = 0;
private int[] elem;
private int head = 0, tail = 0;
public BlockingQueue1(int cap) {
elem = new int[cap];
}
public synchronized void put(int val) {
while (size == elem.length) {
// 队列满
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
size++;
// 队尾进
elem[tail++] = val;
if (tail >= elem.length) {
tail = 0;
}
this.notify();
}
public synchronized int get() {
while (size == 0) {
// 队列为空
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
size--;
// 队头出
int val = elem[head++];
if (head >= elem.length) {
head = 0;
}
this.notify();
return val;
}
}