一句话说透Java里面的ArrayBlockingQueue

150 阅读3分钟

ArrayBlockingQueue 是 Java 并发包中的一个线程安全、基于数组实现的有界阻塞队列,适用于生产者-消费者模型。以下是其核心特性及工作原理的通俗解析:


一、核心特点

  1. 有界队列

    • 创建时需指定固定容量,队列满时无法添加新元素,避免无限制内存消耗。
    • 示例:new ArrayBlockingQueue<>(100); 表示最多容纳100个元素。
  2. 阻塞操作

    • 队列满时:生产者线程调用 put() 会被阻塞,直到队列有空位。
    • 队列空时:消费者线程调用 take() 会被阻塞,直到队列有新元素。
  3. FIFO 顺序

    • 先进先出,保证元素按入队顺序处理。
  4. 线程安全

    • 内部通过 ReentrantLock 和 Condition 机制实现同步。

二、底层实现

  1. 数据结构

    • 循环数组:元素存储在固定大小的数组中,通过头尾指针实现循环利用。
    • 示例:当数组尾部填满时,新元素会插入到数组头部(若有空位)。
  2. 同步机制

    • 一把锁(ReentrantLock) :控制所有入队和出队操作。

    • 两个条件变量(Condition)

      • notEmpty:队列非空时唤醒消费者。
      • notFull:队列非满时唤醒生产者。

三、核心方法

方法行为
put(E e)队列满时阻塞,直到有空位插入。
take()队列空时阻塞,直到有元素可取出。
offer(E e)队列满时直接返回 false,不阻塞。
poll()队列空时直接返回 null,不阻塞。

四、适用场景

  1. 固定资源池

    • 如数据库连接池,控制同时使用的连接数。

    BlockingQueue<Connection> pool = new ArrayBlockingQueue<>(10);  
    
  2. 流量削峰

    • 缓冲突发请求,防止系统过载。

    // 生产者快速接收请求,消费者按处理能力消费  
    BlockingQueue<Request> queue = new ArrayBlockingQueue<>(1000);  
    
  3. 任务调度

    • 多线程间传递任务,确保顺序执行。

    ExecutorService executor = Executors.newFixedThreadPool(4);  
    BlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(100);  
    

五、性能与权衡

优点缺点
内存紧凑(数组连续存储)并发性能较低(单锁机制)
避免动态扩容开销容量固定,需提前合理规划
严格FIFO顺序高竞争下吞吐量不如双锁队列

六、与 LinkedBlockingQueue 对比

对比项ArrayBlockingQueueLinkedBlockingQueue
底层结构数组链表
锁机制单锁(生产消费互斥)双锁(生产消费可并行)
内存占用更小(无节点指针开销)更大(每个元素需额外指针)
吞吐量低至中(单锁)高(双锁并发)

七、代码示例

public class ProducerConsumerExample {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

        // 生产者
        new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    queue.put(i); // 队列满时阻塞
                    System.out.println("Produced: " + i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();

        // 消费者
        new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    int value = queue.take(); // 队列空时阻塞
                    System.out.println("Consumed: " + value);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
    }
}

八、总结

ArrayBlockingQueue 是一个简单可靠的有界阻塞队列,适合对内存敏感、需严格容量控制的场景。其单锁机制虽限制了并发性能,但通过条件变量实现了高效的线程协作。在设计中需权衡容量与吞吐量需求,避免队列成为系统瓶颈。

使用口诀
「数组实现容量固,生产消费互阻塞
单锁控制保安全,条件变量唤线程
内存紧凑性能中,适用流量削峰场!」