Java并发容器ArrayBlockingQueue与LinkedBlockingQueue对比PK

一、相同点

  1. 线程安全 :两者都实现了 BlockingQueue 接口,是 Java 并发工具包 (java.util.concurrent) 中的成员,通过内部锁机制保证多线程环境下的线程安全。
  2. 阻塞操作 :都支持阻塞式的插入 (put) 和取出 (take) 操作。当队列满时,put 操作会阻塞等待;当队列空时,take 操作会阻塞等待。这简化了生产者-消费者模型的实现。
  3. FIFO:两者都是基于先进先出 (FIFO) 原则的队列。
  4. 可设定容量:两者都可以在构造时指定队列的容量,以防止无限制增长导致内存耗尽。

二、不同点与实现原理

特性 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 原理

    • 内部是单向链表,有 headlast 两个指针。
    • 使用了两把锁:putLock 用于控制入队,takeLock 用于控制出队。
    • 使用 AtomicInteger (count) 来原子地记录队列元素个数,两把锁需要通过对 count 的操作来协同。
    • 因为两把锁分离,一个线程在 put 的同时,另一个线程可以 take,极大地减少了锁竞争。
    • 其阻塞机制与 ABQ 类似,也是通过 Condition (notFull, notEmpty) 实现的,但这两个 Condition 分别关联在不同的锁上。

三、使用场景

ArrayBlockingQueue

  • 适合生产速度和消费速度相差不大、对性能要求不是极其苛刻的场景。
  • 适合需要严格控制内存、避免生产者生产过快的场景(因为固定容量,可以起到"削峰"作用)。
  • 例如:传统的线程池任务队列、简单的消息传递。

LinkedBlockingQueue

  • 适合高并发生产者和消费者能力不平衡(即一方快一方慢)的场景。
  • 由于其更高的吞吐量,它是 Executors.newFixedThreadPool() 默认使用的任务队列。
  • 例如:高吞吐量的任务调度、消息处理系统。 (更常用)

四、详细示例代码

下面我将提供一些关于 ArrayBlockingQueueLinkedBlockingQueue 数据存取的详细示例代码,包括基本操作和 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;
    }
}

总结

这些示例展示了 ArrayBlockingQueueLinkedBlockingQueue 的各种操作方法:

  1. 添加元素put()(阻塞)、offer()(非阻塞)、add()(抛异常)
  2. 获取元素take()(阻塞)、poll()(非阻塞)、remove()(抛异常)
  3. 检查元素peek()element()
  4. 批量操作drainTo()addAll()
  5. 队列状态size()isEmpty()remainingCapacity()
  6. 高级用法:迭代器遍历、批量处理

在 Spring Boot 环境中,我们可以将这些队列配置为 Bean,然后在服务类中注入使用,实现生产者和消费者模式。通过合理选择队列类型和操作方法,可以构建出高效、可靠的并发应用程序。

相关推荐
叫我阿柒啊9 分钟前
从Java全栈到前端框架的深度探索
java·微服务·typescript·vue3·springboot·前端开发·全栈开发
架构师沉默1 小时前
Java 开发者别忽略 return!这 11 种写法你写对了吗?
java·后端·架构
RainbowJie11 小时前
Gemini CLI 与 MCP 服务器:释放本地工具的强大潜力
java·服务器·spring boot·后端·python·单元测试·maven
毕设源码尹学长1 小时前
计算机毕业设计 java 血液中心服务系统 基于 Java 的血液管理平台Java 开发的血液服务系统
java·开发语言·课程设计
lumi.2 小时前
2.3零基础玩转uni-app轮播图:从入门到精通 (咸虾米总结)
java·开发语言·前端·vue.js·微信小程序·uni-app·vue
mask哥2 小时前
详解flink SQL基础(四)
java·大数据·数据库·sql·微服务·flink
灰原喜欢柯南2 小时前
Spring Boot 自动配置全流程深度解析
java·spring boot·后端
Code_Artist3 小时前
[Java并发编程]4.阻塞队列
java·数据结构·后端
心月狐的流火号3 小时前
Java NIO Selector 源码分析
java