一、相同点
- 线程安全 :两者都实现了
BlockingQueue
接口,是 Java 并发工具包 (java.util.concurrent
) 中的成员,通过内部锁机制保证多线程环境下的线程安全。 - 阻塞操作 :都支持阻塞式的插入 (
put
) 和取出 (take
) 操作。当队列满时,put
操作会阻塞等待;当队列空时,take
操作会阻塞等待。这简化了生产者-消费者模型的实现。 - FIFO:两者都是基于先进先出 (FIFO) 原则的队列。
- 可设定容量:两者都可以在构造时指定队列的容量,以防止无限制增长导致内存耗尽。
二、不同点与实现原理
特性 | ArrayBlockingQueue (ABQ) | LinkedBlockingQueue (LBQ) |
---|---|---|
底层数据结构 | 一个定长的对象数组 | 一个由 Node 节点组成的单向链表 |
队列容量 | 固定容量,构造函数必须指定 | 可选固定或无界 。默认无界 (Integer.MAX_VALUE ),构造时也可指定容量 |
锁机制 | 一把锁(ReentrantLock) 控制所有的入队和出队操作。生产者和消费者无法真正并行。 | 两把锁 :putLock (入队锁) 和 takeLock (出队锁)。生产者和消费者可以更高程度地并行,吞吐量通常更高。 |
内存预分配 | 构造时即分配固定大小的数组内存 | 动态分配节点内存。每次入队需创建新节点,出队需使节点失效以等待GC |
性能特点 | 内存使用固定,读写操作性能稳定。但由于单锁,在高并发竞争下性能可能成为瓶颈。 | 吞吐量通常更高,尤其是在高并发环境下,因为入队和出队操作可以并行。但存在频繁的节点创建和GC开销。 |
原理深入:
-
ArrayBlockingQueue 原理:
- 内部维护一个定长数组
final Object[] items
和两个索引:putIndex
(下一个入队位置) 和takeIndex
(下一个出队位置)。 - 使用一个
ReentrantLock
(lock
) 和该锁产生的两个Condition
(notEmpty
,notFull
) 来控制并发和阻塞。 - 当队列满时,生产者线程在
notFull
条件上等待;当消费者消费一个元素后,会唤醒在notFull
上等待的线程。 - 当队列空时,消费者线程在
notEmpty
条件上等待;当生产者生产一个元素后,会唤醒在notEmpty
上等待的线程。
- 内部维护一个定长数组
-
LinkedBlockingQueue 原理:
- 内部是单向链表,有
head
和last
两个指针。 - 使用了两把锁:
putLock
用于控制入队,takeLock
用于控制出队。 - 使用
AtomicInteger
(count
) 来原子地记录队列元素个数,两把锁需要通过对count
的操作来协同。 - 因为两把锁分离,一个线程在
put
的同时,另一个线程可以take
,极大地减少了锁竞争。 - 其阻塞机制与 ABQ 类似,也是通过
Condition
(notFull
,notEmpty
) 实现的,但这两个Condition
分别关联在不同的锁上。
- 内部是单向链表,有
三、使用场景
ArrayBlockingQueue:
- 适合生产速度和消费速度相差不大、对性能要求不是极其苛刻的场景。
- 适合需要严格控制内存、避免生产者生产过快的场景(因为固定容量,可以起到"削峰"作用)。
- 例如:传统的线程池任务队列、简单的消息传递。
LinkedBlockingQueue:
- 适合高并发 、生产者和消费者能力不平衡(即一方快一方慢)的场景。
- 由于其更高的吞吐量,它是
Executors.newFixedThreadPool()
默认使用的任务队列。 - 例如:高吞吐量的任务调度、消息处理系统。 (更常用)
四、详细示例代码
下面我将提供一些关于 ArrayBlockingQueue
和 LinkedBlockingQueue
数据存取的详细示例代码,包括基本操作和 Spring Boot 环境下的使用。
1. 基本操作示例
1.1 ArrayBlockingQueue 基本操作
arduino
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class ArrayBlockingQueueExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个容量为3的ArrayBlockingQueue
BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
// 1. 添加元素
System.out.println("=== 添加元素 ===");
queue.put("Item1"); // 阻塞式添加
queue.offer("Item2"); // 非阻塞式添加,返回boolean
boolean success = queue.offer("Item3", 1, TimeUnit.SECONDS); // 带超时的添加
System.out.println("添加Item3是否成功: " + success);
// 队列已满,尝试添加会阻塞或返回false
boolean failed = queue.offer("Item4"); // 立即返回false
System.out.println("添加Item4是否成功: " + failed);
// 2. 查看元素
System.out.println("\n=== 查看元素 ===");
System.out.println("队列头部元素: " + queue.peek()); // 查看但不移除
System.out.println("队列大小: " + queue.size());
// 3. 移除元素
System.out.println("\n=== 移除元素 ===");
System.out.println("取出元素: " + queue.take()); // 阻塞式取出
System.out.println("取出元素: " + queue.poll()); // 非阻塞式取出
System.out.println("超时取出: " + queue.poll(1, TimeUnit.SECONDS)); // 带超时的取出
// 队列已空,尝试取出会阻塞或返回null
System.out.println("尝试取出空队列: " + queue.poll());
// 4. 检查队列状态
System.out.println("\n=== 检查队列状态 ===");
System.out.println("队列是否为空: " + queue.isEmpty());
System.out.println("队列剩余容量: " + queue.remainingCapacity());
// 5. 清空队列
queue.put("NewItem1");
queue.put("NewItem2");
System.out.println("清空前队列大小: " + queue.size());
queue.clear();
System.out.println("清空后队列大小: " + queue.size());
}
}
1.2 LinkedBlockingQueue 基本操作
csharp
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class LinkedBlockingQueueExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个容量为3的LinkedBlockingQueue
LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>(3);
// 1. 添加元素
System.out.println("=== 添加元素 ===");
queue.put("Item1"); // 阻塞式添加
queue.add("Item2"); // 非阻塞式添加,队列满时抛出异常
boolean success = queue.offer("Item3", 1, TimeUnit.SECONDS); // 带超时的添加
System.out.println("添加Item3是否成功: " + success);
// 队列已满,尝试添加会阻塞或抛出异常
try {
queue.add("Item4"); // 将抛出IllegalStateException
} catch (IllegalStateException e) {
System.out.println("添加Item4时抛出异常: " + e.getMessage());
}
// 2. 查看元素
System.out.println("\n=== 查看元素 ===");
System.out.println("队列头部元素: " + queue.element()); // 查看但不移除,空队列抛出异常
System.out.println("队列大小: " + queue.size());
// 3. 移除元素
System.out.println("\n=== 移除元素 ===");
System.out.println("取出元素: " + queue.take()); // 阻塞式取出
System.out.println("取出元素: " + queue.remove()); // 非阻塞式取出,空队列抛出异常
System.out.println("超时取出: " + queue.poll(1, TimeUnit.SECONDS)); // 带超时的取出
// 队列已空,尝试取出会阻塞或抛出异常
try {
System.out.println("尝试取出空队列: " + queue.remove());
} catch (Exception e) {
System.out.println("取出空队列时抛出异常: " + e.getClass().getSimpleName());
}
// 4. 批量操作
System.out.println("\n=== 批量操作 ===");
queue.put("A");
queue.put("B");
queue.put("C");
// 批量移除
System.out.println("移除指定元素B: " + queue.remove("B"));
// 批量添加
boolean allAdded = queue.addAll(java.util.Arrays.asList("D", "E"));
System.out.println("批量添加是否成功: " + allAdded);
System.out.println("当前队列: " + queue.toString());
}
}
2. Spring Boot 中的生产者-消费者示例
2.1 配置类
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@Configuration
public class QueueConfig {
// 配置一个ArrayBlockingQueue
@Bean
public BlockingQueue<String> arrayBlockingQueue() {
return new ArrayBlockingQueue<>(100);
}
// 配置一个LinkedBlockingQueue
@Bean
public BlockingQueue<String> linkedBlockingQueue() {
return new LinkedBlockingQueue<>(100);
}
}
2.2 生产者服务
java
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
@Service
public class MessageProducerService {
private final BlockingQueue<String> arrayQueue;
private final BlockingQueue<String> linkedQueue;
private final AtomicLong messageId = new AtomicLong(0);
public MessageProducerService(
@Qualifier("arrayBlockingQueue") BlockingQueue<String> arrayQueue,
@Qualifier("linkedBlockingQueue") BlockingQueue<String> linkedQueue) {
this.arrayQueue = arrayQueue;
this.linkedQueue = linkedQueue;
}
/**
* 向ArrayBlockingQueue发送消息
*/
public boolean sendToArrayQueue(String message) throws InterruptedException {
String fullMessage = "MSG-" + messageId.incrementAndGet() + ": " + message;
// 使用put方法,如果队列满则阻塞
arrayQueue.put(fullMessage);
return true;
}
/**
* 向LinkedBlockingQueue发送消息(带超时)
*/
public boolean sendToLinkedQueue(String message, long timeoutMs) throws InterruptedException {
String fullMessage = "MSG-" + messageId.incrementAndGet() + ": " + message;
// 使用offer方法,如果队列满则等待指定时间
return linkedQueue.offer(fullMessage, timeoutMs, TimeUnit.MILLISECONDS);
}
/**
* 尝试向ArrayBlockingQueue发送消息(非阻塞)
*/
public boolean trySendToArrayQueue(String message) {
String fullMessage = "MSG-" + messageId.incrementAndGet() + ": " + message;
// 使用offer方法,立即返回结果
return arrayQueue.offer(fullMessage);
}
}
2.3 消费者服务
typescript
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.BlockingQueue;
@Service
public class MessageConsumerService {
private final BlockingQueue<String> arrayQueue;
private final BlockingQueue<String> linkedQueue;
private volatile boolean running = true;
public MessageConsumerService(
@Qualifier("arrayBlockingQueue") BlockingQueue<String> arrayQueue,
@Qualifier("linkedBlockingQueue") BlockingQueue<String> linkedQueue) {
this.arrayQueue = arrayQueue;
this.linkedQueue = linkedQueue;
}
/**
* 启动消费者
*/
@PostConstruct
public void init() {
startArrayQueueConsumer();
startLinkedQueueConsumer();
}
/**
* 启动ArrayBlockingQueue消费者
*/
@Async
public void startArrayQueueConsumer() {
while (running) {
try {
// 使用take方法,如果队列空则阻塞
String message = arrayQueue.take();
processMessage("ArrayQueue", message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
/**
* 启动LinkedBlockingQueue消费者
*/
@Async
public void startLinkedQueueConsumer() {
while (running) {
try {
// 使用poll方法,带超时时间
String message = linkedQueue.poll(100, TimeUnit.MILLISECONDS);
if (message != null) {
processMessage("LinkedQueue", message);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
/**
* 处理消息
*/
private void processMessage(String queueType, String message) {
System.out.println("[" + queueType + "] 处理消息: " + message + " (线程: " + Thread.currentThread().getName() + ")");
// 模拟处理耗时
try {
Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* 停止消费者
*/
public void stopConsumers() {
running = false;
}
/**
* 批量获取消息
*/
public void drainArrayQueueToCollection(java.util.Collection<String> collection) {
// 一次性取出所有可用元素到指定集合
arrayQueue.drainTo(collection);
}
/**
* 批量获取最多指定数量的消息
*/
public void drainLinkedQueueToCollection(java.util.Collection<String> collection, int maxElements) {
// 一次性取出最多maxElements个元素到指定集合
linkedQueue.drainTo(collection, maxElements);
}
}
2.4 REST 控制器
java
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/queue")
public class QueueController {
private final MessageProducerService producerService;
private final MessageConsumerService consumerService;
public QueueController(MessageProducerService producerService,
MessageConsumerService consumerService) {
this.producerService = producerService;
this.consumerService = consumerService;
}
/**
* 发送消息到ArrayBlockingQueue
*/
@PostMapping("/array/send")
public String sendToArrayQueue(@RequestParam String message) throws InterruptedException {
producerService.sendToArrayQueue(message);
return "消息已发送到ArrayBlockingQueue: " + message;
}
/**
* 尝试发送消息到ArrayBlockingQueue(非阻塞)
*/
@PostMapping("/array/try-send")
public String trySendToArrayQueue(@RequestParam String message) {
boolean success = producerService.trySendToArrayQueue(message);
return success ? "消息发送成功: " + message : "队列已满,发送失败: " + message;
}
/**
* 发送消息到LinkedBlockingQueue
*/
@PostMapping("/linked/send")
public String sendToLinkedQueue(@RequestParam String message,
@RequestParam(defaultValue = "1000") long timeout) throws InterruptedException {
boolean success = producerService.sendToLinkedQueue(message, timeout);
return success ? "消息发送成功: " + message : "发送超时,队列可能已满: " + message;
}
/**
* 批量获取ArrayBlockingQueue中的消息
*/
@GetMapping("/array/drain")
public List<String> drainArrayQueue() {
List<String> messages = new ArrayList<>();
consumerService.drainArrayQueueToCollection(messages);
return messages;
}
/**
* 批量获取LinkedBlockingQueue中的消息(最多指定数量)
*/
@GetMapping("/linked/drain")
public List<String> drainLinkedQueue(@RequestParam(defaultValue = "10") int maxElements) {
List<String> messages = new ArrayList<>();
consumerService.drainLinkedQueueToCollection(messages, maxElements);
return messages;
}
/**
* 停止消费者
*/
@PostMapping("/consumers/stop")
public String stopConsumers() {
consumerService.stopConsumers();
return "消费者已停止";
}
}
3. 高级用法示例
3.1 使用 Drain 方法批量处理
java
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
@Service
public class BatchProcessorService {
private final BlockingQueue<String> arrayQueue;
public BatchProcessorService(@Qualifier("arrayBlockingQueue") BlockingQueue<String> arrayQueue) {
this.arrayQueue = arrayQueue;
}
/**
* 批量处理消息
*/
public void processInBatches() {
List<String> batch = new ArrayList<>(50); // 预分配容量
while (true) {
// 取出当前所有可用元素,但最多取50个
int drained = arrayQueue.drainTo(batch, 50);
if (drained > 0) {
processBatch(batch);
batch.clear(); // 清空列表以备下次使用
} else {
// 如果没有元素,等待一段时间再检查
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
}
/**
* 处理一批消息
*/
private void processBatch(List<String> batch) {
System.out.println("处理批量消息,数量: " + batch.size());
// 这里可以实现批量处理逻辑,如批量写入数据库等
for (String message : batch) {
System.out.println("处理: " + message);
}
}
}
3.2 使用迭代器遍历队列
arduino
import org.springframework.stereotype.Service;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
@Service
public class QueueInspectionService {
private final BlockingQueue<String> linkedQueue;
public QueueInspectionService(@Qualifier("linkedBlockingQueue") BlockingQueue<String> linkedQueue) {
this.linkedQueue = linkedQueue;
}
/**
* 检查队列内容(不移除元素)
*/
public List<String> inspectQueue() {
List<String> snapshot = new ArrayList<>();
// 使用迭代器遍历队列(不会移除元素)
Iterator<String> iterator = linkedQueue.iterator();
while (iterator.hasNext()) {
snapshot.add(iterator.next());
}
return snapshot;
}
/**
* 查找包含特定文本的消息
*/
public List<String> findMessagesContaining(String text) {
List<String> results = new ArrayList<>();
// 使用迭代器遍历并过滤
Iterator<String> iterator = linkedQueue.iterator();
while (iterator.hasNext()) {
String message = iterator.next();
if (message.contains(text)) {
results.add(message);
}
}
return results;
}
}
总结
这些示例展示了 ArrayBlockingQueue
和 LinkedBlockingQueue
的各种操作方法:
- 添加元素 :
put()
(阻塞)、offer()
(非阻塞)、add()
(抛异常) - 获取元素 :
take()
(阻塞)、poll()
(非阻塞)、remove()
(抛异常) - 检查元素 :
peek()
、element()
- 批量操作 :
drainTo()
、addAll()
- 队列状态 :
size()
、isEmpty()
、remainingCapacity()
- 高级用法:迭代器遍历、批量处理
在 Spring Boot 环境中,我们可以将这些队列配置为 Bean,然后在服务类中注入使用,实现生产者和消费者模式。通过合理选择队列类型和操作方法,可以构建出高效、可靠的并发应用程序。