1 概述和架构
在并发编程中,队列和栈是两种常见的数据结构,它们在不同的场景下有不同的应用。阻塞队列是一种特殊的队列,它在多线程环境中提供了线程安全的操作,并且在队列为空或满时能够阻塞线程,从而避免资源竞争和数据不一致的问题。
1.1 队列和栈
1.1.1 队列(Queue)
队列是一种先进先出(FIFO, First In First Out)的数据结构。元素从队列的一端(称为队尾)插入,从另一端(称为队头)移除。队列常用于任务调度、消息传递等场景。
- 先进先出:最先进入队列的元素最先被移除。
- 应用场景:任务调度、消息队列、缓冲区等。
1.1.2 栈(Stack)
栈是一种后进先出(LIFO, Last In First Out)的数据结构。元素从栈的同一端(称为栈顶)插入和移除。栈常用于函数调用、表达式求值等场景。
- 后进先出:最后进入栈的元素最先被移除。
- 应用场景:函数调用栈、表达式求值、撤销操作等。
1.2 阻塞队列(Blocking Queue)
阻塞队列是一种特殊的队列,它在多线程环境中提供了线程安全的操作,并且在队列为空或满时能够阻塞线程。阻塞队列常用于生产者-消费者模型中,生产者线程向队列中添加元素,消费者线程从队列中移除元素。
1.2.1 阻塞队列的特点
-
阻塞操作:
- 当队列是空的,从队列中获取元素的操作将会被阻塞,直到其他线程往队列中插入新的元素。
- 当队列是满的,从队列中添加元素的操作将会被阻塞,直到其他线程从队列中移除一个或多个元素或者完全清空,使队列变得空闲起来并后续新增。
-
线程安全:阻塞队列提供了线程安全的操作,多个线程可以同时对队列进行操作而不会导致数据不一致。
1.3 阻塞队列的架构
阻塞队列的架构通常包括以下几个核心组件:
- 队列容器:用于存储元素的数据结构,可以是数组、链表等。
- 锁机制:用于保证线程安全的同步机制,通常使用
ReentrantLock
或其他同步工具。 - 条件变量:用于线程间的通信,当队列为空或满时,线程会被阻塞,直到满足条件。
2 阻塞队列的分类和核心方法
阻塞队列是并发编程中常用的数据结构,它提供了线程安全的操作,并且在队列为空或满时能够阻塞线程。Java 提供了多种阻塞队列的实现,每种实现都有其特定的应用场景。
2.1 阻塞队列分类
Java 提供了以下几种常见的阻塞队列实现:
- ArrayBlockingQueue:基于数组的有界阻塞队列,容量固定,支持公平和非公平锁。
- LinkedBlockingQueue:基于链表的可选有界阻塞队列,容量可以指定或不指定(默认是
Integer.MAX_VALUE
)。 - DelayQueue:无界阻塞队列,元素只有在延迟期满时才能被取出。
- PriorityBlockingQueue:无界阻塞队列,元素按照优先级顺序取出。
- SynchronousQueue:不存储元素的阻塞队列,每个插入操作必须等待一个移除操作,反之亦然。
- LinkedTransferQueue:基于链表的无界阻塞队列,支持高效的插入和移除操作。
2.2 核心方法
阻塞队列提供了多种操作方法,根据不同的需求,可以选择不同的方法。以下是阻塞队列的核心方法分类:
2.2.1 抛出异常
当操作无法立即执行时,抛出异常。
- 插入:
add(e)
- 如果队列已满,抛出
IllegalStateException
。
- 如果队列已满,抛出
- 移除:
remove()
- 如果队列为空,抛出
NoSuchElementException
。
- 如果队列为空,抛出
- 检查:
element()
- 返回队列头部的元素,如果队列为空,抛出
NoSuchElementException
。
- 返回队列头部的元素,如果队列为空,抛出
2.2.2 特殊值
当操作无法立即执行时,返回特殊值(如 null
或 false
)。
- 插入:
offer(e)
- 如果队列已满,返回
false
。
- 如果队列已满,返回
- 移除:
poll()
- 如果队列为空,返回
null
。
- 如果队列为空,返回
- 检查:
peek()
- 返回队列头部的元素,如果队列为空,返回
null
。
- 返回队列头部的元素,如果队列为空,返回
2.2.3 阻塞
当操作无法立即执行时,阻塞线程,直到操作可以执行。
- 插入:
put(e)
- 如果队列已满,阻塞线程,直到队列有空闲空间。
- 移除:
take()
- 如果队列为空,阻塞线程,直到队列有元素。
- 检查:不可用
- 阻塞队列不提供阻塞的检查方法。
2.2.4 超时
当操作无法立即执行时,阻塞线程,直到操作可以执行或超时。
- 插入:
offer(e, time, unit)
- 如果队列已满,阻塞线程,直到队列有空闲空间或超时。
- 移除:
poll(time, unit)
- 如果队列为空,阻塞线程,直到队列有元素或超时。
- 检查:不可用
- 阻塞队列不提供超时的检查方法。
3 核心方法演示
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// 创建阻塞队列
BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
// 第一组
/* System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
System.out.println(blockingQueue.element());
// System.out.println(blockingQueue.add("w"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());*/
// 第二组
/* System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("www"));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());*/
// 第三组
/* blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
// blockingQueue.put("w");
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());*/
// 第四组
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
System.out.println(blockingQueue.offer("w",3L, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(3L,TimeUnit.SECONDS));
}
}