Java 提供了多种线程安全的队列实现,它们位于 java.util.concurrent (JUC) 包下,这些队列解决了多线程环境下对共享数据进行访问时的核心问题:竞态条件。

(图片来源网络,侵删)
选择哪种线程安全队列,主要取决于你的业务需求,特别是生产者和消费者的行为模式,我们可以从两个维度来分类它们:
- 阻塞 vs. 非阻塞:当队列满或空时,操作是等待(阻塞)还是立即返回?
- 有界 vs. 无界:队列是否有一个固定的容量上限?
下面是核心的几种线程安全队列,以及它们的特点和适用场景。
核心线程安全队列概览
| 队列名称 | 分类 | 数据结构 | 特点 | 适用场景 |
|---|---|---|---|---|
BlockingQueue |
接口 | - | 阻塞队列的核心接口,当队列满时,put 会阻塞;当队列空时,take 会阻塞。 |
生产者-消费者模型的首选。 |
ArrayBlockingQueue |
有界阻塞队列 | 数组 | 有固定容量,基于数组实现,公平性可选(公平锁 vs. 非公平锁)。 | 容量固定的场景,对内存使用有要求。 |
LinkedBlockingQueue |
可选有界阻塞队列 | 链表 | 默认容量为 Integer.MAX_VALUE(无界),但可指定容量,吞吐量通常高于 ArrayBlockingQueue。 |
默认选择,高并发场景,如线程池。 |
PriorityBlockingQueue |
无界阻塞队列 | 堆 | 按照元素的优先级排序,无界,可能因元素过多导致 OOM。 |
需要按优先级处理任务,且任务量可控的场景。 |
SynchronousQueue |
无容量阻塞队列 | - | 不存储元素,每个 put 操作必须等待一个 take 操作,反之亦然。 |
传递性场景,如 Executors.newCachedThreadPool。 |
ConcurrentLinkedQueue |
非阻塞无界队列 | 链表 | 基于 CAS (Compare-And-Swap) 实现的高性能无锁队列。 | 高并发、低延迟、不需要阻塞的场景。 |
LinkedBlockingDeque |
可选有界双端阻塞队列 | 链表 | 双端阻塞队列,支持在两端进行插入和移除操作。 | 工作窃取线程池,或需要双向通信的生产者-消费者模型。 |
详细解析
BlockingQueue (阻塞队列接口)
这是所有阻塞队列的父接口,定义了阻塞操作的核心方法:
- 阻塞方法:
put(E e): 将元素插入队列,如果队列已满,则阻塞直到有空间可用。take(): 获取并移除队列头部的元素,如果队列为空,则阻塞直到有元素可用。
- 超时方法:
offer(E e, long timeout, TimeUnit unit): 尝试插入元素,如果在指定时间内仍无法插入,则返回false。poll(long timeout, TimeUnit unit): 尝试获取元素,如果在指定时间内仍无法获取,则返回null。
- 非阻塞方法:
add(E e): 如果队列满,则抛出IllegalStateException。offer(E e): 如果队列满,则返回false。remove(): 如果队列空,则抛出NoSuchElementException。poll(): 如果队列空,则返回null。
核心思想:通过阻塞机制,让生产者和消费者在无法操作时让出 CPU,避免了无效的自旋和 CPU 资源浪费,非常适合构建稳定的生产者-消费者系统。

(图片来源网络,侵删)
ArrayBlockingQueue (有界阻塞队列)
- 特点:
- 有界:必须在创建时指定容量。
- 数组实现:内存是连续的,缓存局部性好。
- 公平性:构造函数可以接受一个
fair参数,如果为true,则按照 FIFO 的顺序给等待的线程分配锁,避免线程饥饿;如果为false(默认),则使用非公平锁,吞吐量更高。
- 适用场景:
- 当你需要一个容量固定的队列来防止系统资源(内存)被耗尽时。
- 对性能要求极高,且可以牺牲一定的公平性来换取吞吐量。
// 创建一个容量为 10 的公平阻塞队列 BlockingQueue<String> queue = new ArrayBlockingQueue<>(10, true);
LinkedBlockingQueue (可选有界阻塞队列)
- 特点:
- 链表实现:节点是动态创建的,没有固定容量(默认
Integer.MAX_VALUE)。 - 高吞吐量:通常比
ArrayBlockingQueue性能更好,因为它的锁是分离的,它维护了两个独立的锁:一个用于put操作,一个用于take操作,这意味着生产者和消费者可以同时操作队列,并发性更高。 - 可指定容量:虽然默认无界,但强烈建议在生产环境中指定一个合理的容量,防止
OOM。
- 链表实现:节点是动态创建的,没有固定容量(默认
- 适用场景:
- Java 线程池的默认队列(
ThreadPoolExecutor的newFixedThreadPool和newSingleThreadExecutor)。 - 大多数生产者-消费者场景的首选,因为它在性能和灵活性之间取得了很好的平衡。
- Java 线程池的默认队列(
// 创建一个容量为 100 的阻塞队列 BlockingQueue<Task> queue = new LinkedBlockingQueue<>(100);
PriorityBlockingQueue (无界阻塞优先队列)
- 特点:
- 无界:内部容量可以无限增长,可能导致
OutOfMemoryError。 - 优先级排序:元素必须实现
Comparable接口,或者在构造时提供Comparator,队列头部的元素是“优先级最高”的。 - 阻塞特性:和
BlockingQueue一样,put和take等操作会阻塞。
- 无界:内部容量可以无限增长,可能导致
- 适用场景:
- 任务有优先级,需要优先处理高优先级任务的场景,如任务调度系统。
- 警告:必须严格控制任务的提交速率,确保队列不会无限增长。
// 创建一个无界的优先级队列 PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
SynchronousQueue (同步队列)
- 特点:
- 容量为 0:它不存储任何元素,可以把它想象成一个“交接点”。
- 一对一传递:一个线程调用
put(e)时,必须等待另一个线程调用take()来接收这个元素e,反之亦然。 - 不消费空间:在
Executors.newCachedThreadPool中使用它时,线程池的线程数可以根据任务量动态增长,因为任务不会在队列中积压。
- 适用场景:
- 高并发、轻量级的传递场景。
ThreadPoolExecutor的newCachedThreadPool。- 两个线程之间的直接数据交换。
// 创建一个同步队列 BlockingQueue<Integer> queue = new SynchronousQueue<>();
ConcurrentLinkedQueue (非阻塞无界队列)
- 特点:
- 非阻塞:所有操作(
add,offer,poll等)都是非阻塞的,如果操作失败(如poll空队列),它会立即返回,不会等待。 - 无锁:内部使用 CAS 算法来保证线程安全,避免了锁带来的开销,在高并发下性能极高。
- 无界:同样有
OOM风险。
- 非阻塞:所有操作(
- 适用场景:
- 高并发、低延迟的场景,不需要等待。
- 生产者和消费者的处理速度非常快,不需要队列来缓冲。
- 需要快速遍历队列(
iterator()是弱一致性的,不保证能看到所有最新元素)。
// 创建一个非阻塞并发队列 Queue<String> queue = new ConcurrentLinkedQueue<>();
LinkedBlockingDeque (双端阻塞队列)
- 特点:
- 双端操作:支持在队列的两头(
head和tail)进行addFirst/Last,pollFirst/Last等操作。 - 阻塞特性:继承了
BlockingQueue的所有阻塞功能。 - 锁分离:和
LinkedBlockingQueue一样,它也使用了分离的锁,但因为是双端,所以需要两把锁(一个用于头节点,一个用于尾节点),并发性依然很好。
- 双端操作:支持在队列的两头(
- 适用场景:
- 工作窃取线程池(
ForkJoinPool),工作线程可以从其他线程的队列尾部“窃取”任务,减少竞争。 - 需要双向通信的生产者-消费者模型。
- 工作窃取线程池(
// 创建一个容量为 100 的双端阻塞队列 BlockingDeque<String> deque = new LinkedBlockingDeque<>(100);
如何选择?(决策树)
你的场景需要阻塞吗?
├── 是 -> 继续选择阻塞队列
│ ├── 你的队列需要固定容量吗?
│ │ ├── 是 -> 选择 ArrayBlockingQueue
│ │ │ └── 对公平性有要求吗?
│ │ │ ├── 是 -> new ArrayBlockingQueue(capacity, true)
│ │ │ └── 否 -> new ArrayBlockingQueue(capacity, false) (默认)
│ │ └── 否 (可选容量或无界) -> 选择 LinkedBlockingQueue
│ │ └── 需要双端操作吗?
│ │ ├── 是 -> new LinkedBlockingDeque(capacity)
│ │ └── 否 -> new LinkedBlockingQueue(capacity) (强烈建议指定容量)
│ └── 你的任务需要按优先级处理吗?
│ ├── 是 -> new PriorityBlockingQueue() (注意无界风险)
│ └── 否 -> 你的场景是点对点同步传递吗?
│ ├── 是 -> new SynchronousQueue()
│ └── 否 -> LinkedBlockingQueue 通常是默认的、安全的选择
└── 否 (不需要阻塞,追求极致性能) -> 选择 ConcurrentLinkedQueue
└── 你的队列需要双端操作吗?
├── 是 -> Java 没有内置的 ConcurrentLinkedDeque,可以考虑其他并发集合或自己实现
└── 否 -> new ConcurrentLinkedQueue()
代码示例:经典的生产者-消费者模式
这个例子展示了如何使用 LinkedBlockingQueue 来实现一个线程安全的生产者-消费者模型。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
class Producer implements Runnable {
private final BlockingQueue<String> queue;
public Producer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
for (int i = 1; i <= 10; i++) {
String message = "Message-" + i;
System.out.println("Producer producing: " + message);
queue.put(message); // 如果队列满,这里会阻塞
TimeUnit.SECONDS.sleep(1); // 模拟生产耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Producer was interrupted.");
}
System.out.println("Producer finished.");
}
}
class Consumer implements Runnable {
private final BlockingQueue<String> queue;
public Consumer(BlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) { // 消费者持续运行
String message = queue.take(); // 如果队列空,这里会阻塞
System.out.println("Consumer consuming: " + message);
TimeUnit.MILLISECONDS.sleep(500); // 模拟消费耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Consumer was interrupted.");
}
System.out.println("Consumer finished.");
}
}
public class ProducerConsumerExample {
public static void main(String[] args) {
// 使用 LinkedBlockingQueue 作为共享队列
BlockingQueue<String> queue = new LinkedBlockingQueue<>(5); // 指定容量为5
Thread producerThread = new Thread(new Producer(queue));
Thread consumerThread = new Thread(new Consumer(queue));
producerThread.start();
consumerThread.start();
// 让主线程等待生产者完成
try {
producerThread.join();
// 生产者结束后,中断消费者,让它优雅退出
consumerThread.interrupt();
consumerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
| 特性 | ArrayBlockingQueue |
LinkedBlockingQueue |
ConcurrentLinkedQueue |
|---|---|---|---|
| 阻塞/非阻塞 | 阻塞 | 阻塞 | 非阻塞 |
| 有界/无界 | 有界 | 可选有界/无界 | 无界 |
| 数据结构 | 数组 | 链表 | 链表 |
| 锁机制 | 单锁(公平/非公平可选) | 分离锁(高并发) | CAS(无锁) |
| 核心优势 | 内存紧凑,公平性可控 | 高吞吐量,通用性强 | 极致性能,低延迟 |
| 主要风险 | 队列满/空时的阻塞 | 无界时的 OOM |
无界时的 OOM |
理解这些队列的特性和适用场景,是编写健壮、高效多线程程序的关键一步,在大多数情况下,LinkedBlockingQueue 是一个安全且性能优秀的选择,对于需要极致性能且能处理非阻塞逻辑的场景,ConcurrentLinkedQueue 是不二之选。

(图片来源网络,侵删)
