详解Java中的ArrayBlockingQueue
ArrayBlockingQueue
是 Java 中 java.util.concurrent
包下的一个阻塞队列实现,底层基于数组,是线程安全的。它是一个 有界队列,需要在创建时指定容量大小。此类的主要特性包括:
-
线程安全:
-
使用 独占锁(ReentrantLock) 和 条件变量 来实现线程安全的操作。
-
-
先进先出(FIFO):
-
元素按插入顺序排列,头部是最先插入的元素,尾部是最后插入的元素。
-
-
-
队列容量固定,不能超过初始化时指定的大小。
-
核心构造方法
1 | public ArrayBlockingQueue( int capacity) |
-
创建一个指定容量的队列。
1 | public ArrayBlockingQueue( int capacity, boolean fair) |
-
fair
: 决定锁的公平性,true
表示公平锁,false
为非公平锁(性能更高)。
1 | public ArrayBlockingQueue( int capacity, boolean fair, Collection<? extends E> c) |
-
初始化队列,并将给定集合中的所有元素按迭代顺序添加到队列。
核心字段
1 2 3 4 5 6 7 8 | private final Object[] items; // 存储队列元素的数组 private int takeIndex; // 指向队列头部(下一个被取出的元素) private int putIndex; // 指向队列尾部(下一个插入的位置) private int count; // 队列中当前的元素个数 private final ReentrantLock lock; // 独占锁,保证线程安全 private final Condition notEmpty; // 条件变量:队列不为空的等待条件 private final Condition notFull; // 条件变量:队列未满的等待条件 |
关键方法解读
1. offer(E e)
尝试向队列中插入元素(非阻塞方法)。如果队列已满,返回 false
。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public boolean offer(E e) { if (e == null ) throw new NullPointerException(); final ReentrantLock lock = this .lock; lock.lock(); // 获取锁 try { if (count == items.length) // 队列已满 return false ; enqueue(e); // 插入元素 return true ; } finally { lock.unlock(); // 释放锁 } } |
2. put(E e)
向队列中插入元素,如果队列已满,当前线程阻塞直到队列有空间。
1 2 3 4 5 6 7 8 9 10 11 12 | public void put(E e) throws InterruptedException { if (e == null ) throw new NullPointerException(); final ReentrantLock lock = this .lock; lock.lockInterruptibly(); // 可中断获取锁 try { while (count == items.length) // 队列已满,等待 notFull 信号 notFull.await(); enqueue(e); // 插入元素 } finally { lock.unlock(); // 释放锁 } } |
3. take()
从队列中取出一个元素,如果队列为空,当前线程阻塞直到有可用元素。
1 2 3 4 5 6 7 8 9 10 11 | public E take() throws InterruptedException { final ReentrantLock lock = this .lock; lock.lockInterruptibly(); // 可中断获取锁 try { while (count == 0 ) // 队列为空,等待 notEmpty 信号 notEmpty.await(); return dequeue(); // 获取队头元素 } finally { lock.unlock(); // 释放锁 } } |
4. peek()
非阻塞方法,返回队列头部元素但不移除。如果队列为空,返回 null
。
1 2 3 4 5 6 7 8 9 | public E peek() { final ReentrantLock lock = this .lock; lock.lock(); // 获取锁 try { return (count == 0 ) ? null : itemAt(takeIndex); // 获取头部元素 } finally { lock.unlock(); // 释放锁 } } |
底层核心方法
1. enqueue(E e)
向队列尾部插入元素。
1 2 3 4 5 6 7 8 | private void enqueue(E x) { final Object[] items = this .items; items[putIndex] = x; // 在 putIndex 位置插入元素 if (++putIndex == items.length) // 如果到达数组尾部,回到起始位置(循环数组) putIndex = 0 ; count++; // 更新队列元素个数 notEmpty.signal(); // 唤醒等待 notEmpty 的线程 } |
2. dequeue()
从队列头部取出元素。
1 2 3 4 5 6 7 8 9 10 11 | private E dequeue() { final Object[] items = this .items; @SuppressWarnings ( "unchecked" ) E x = (E) items[takeIndex]; // 获取队列头部的元素 items[takeIndex] = null ; // 释放队列头部的引用 if (++takeIndex == items.length) // 如果到达数组尾部,回到起始位置 takeIndex = 0 ; count--; // 更新队列元素个数 notFull.signal(); // 唤醒等待 notFull 的线程 return x; } |
线程安全性
-
使用 独占锁 来控制对队列的并发访问。
-
条件变量
notEmpty
和notFull
分别控制取元素和插入元素的等待逻辑。 -
锁的公平性(可选)决定线程获取锁的顺序,公平锁更公平但性能稍差。
应用场景
-
生产者-消费者模型
:
-
生产者线程向队列插入元素,消费者线程从队列取出元素。
-
-
任务调度
:
-
用于线程池中存储和分发任务。
-
-
流量控制
:
-
通过有界性限制队列中的元素数量,防止内存溢出。
-
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public static void main(String[] args) { ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>( 5 ); // 生产者线程 new Thread(() -> { try { for ( int i = 1 ; i <= 10 ; i++) { queue.put(i); System.out.println( "Produced: " + i); Thread.sleep( 100 ); } } catch (InterruptedException e) { e.printStackTrace(); } }).start(); // 消费者线程 new Thread(() -> { try { while ( true ) { Integer value = queue.take(); System.out.println( "Consumed: " + value); Thread.sleep( 200 ); } } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期