根本区别在于设计目标和内存语义:ConcurrentLinkedQueue是无锁、弱一致性、高吞吐的非阻塞队列;LinkedBlockingQueue是基于ReentrantLock+Condition的阻塞队列,提供强FIFO和背压能力。

ConcurrentLinkedQueue 和 LinkedBlockingQueue 的核心区别在哪
根本区别不在“是否阻塞”,而在于设计目标和内存语义:ConcurrentLinkedQueue 是无锁(lock-free)、弱一致性、高吞吐的非阻塞队列,适合纯生产消费速率接近的场景;LinkedBlockingQueue 是基于 ReentrantLock + Condition 的阻塞队列,提供强 FIFO 保证和显式等待能力,适合需要背压(backpressure)或消费者可能长期空闲的场景。
常见误用:用 ConcurrentLinkedQueue 做“消费者等数据”逻辑——它没有 poll(long, TimeUnit),只能轮询 poll() 或搭配 Thread.sleep(),既浪费 CPU 又无法精准响应中断。
-
ConcurrentLinkedQueue的size()不可靠,返回的是近似值,不能用于条件判断(比如if (q.size() > 0)) -
LinkedBlockingQueue默认构造时容量为Integer.MAX_VALUE,看似“无界”,但实际仍会 OOM;建议显式传入容量(如new LinkedBlockingQueue(1024))以启用背压 - 二者都线程安全,但
ConcurrentLinkedQueue不保证迭代器的实时一致性(iterator()返回的迭代器可能跳过刚入队元素或重复遍历)
用 BlockingQueue 实现生产者消费者时,如何正确响应中断
关键在所有阻塞调用处必须捕获 InterruptedException 并主动退出循环,否则线程会永远卡在 take() 或 put() 上,导致 shutdown 失败。
while (!Thread.currentThread().isInterrupted()) {
try {
String task = queue.take(); // 可能被中断
process(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
break;
}
}
注意两点:
立即学习“Java免费学习笔记(深入)”;
- 不要只写
catch (InterruptedException e) { return; }—— 这会丢失中断状态,上层无法感知该线程已终止 -
put()和take()都会响应中断;但offer()和poll()不会阻塞,也不抛InterruptedException - 如果生产者/消费者是
ExecutorService提交的Runnable,需确保其内部也遵循中断传播规则,否则shutdownNow()无效
ArrayBlockingQueue 的公平性参数到底影响什么
fair 参数控制的是**等待线程获取锁的顺序**,不是“队列操作的公平性”。开启公平模式(true)后,ReentrantLock 使用 FIFO 等待队列,避免插队;关闭(false,默认)则允许抢占,吞吐略高但可能饥饿。
它不影响 ArrayBlockingQueue 自身的存储结构(仍是数组+两个指针),也不改变 offer()/poll() 的行为逻辑——这些方法本身不阻塞,自然不参与公平调度。
- 公平模式下,大量争抢时平均延迟更稳定,但吞吐下降约 10%~20%,实测差异取决于线程数与操作频率
- 仅当队列满(
put)或空(take)引发线程挂起时,fair才起作用 - 不要为了“看起来更公平”盲目开 fair,尤其在高性能日志采集等场景,默认非公平更合适
为什么 SynchronousQueue 不适合做“缓冲队列”
SynchronousQueue 根本没有内部容量,每个 put() 必须立刻匹配一个 take(),反之亦然。它不是“容量为 1 的队列”,而是“手递手通道”。把它当缓冲用,等于把生产者和消费者强制同步耦合。
典型症状:生产者调用 put() 后线程挂起,直到有消费者恰好执行 take();若消费者处理慢或暂时不存在,生产者就卡死——这违背了“解耦”初衷。
- 适用场景只有两类:线程池的直接交接(如
Executors.newCachedThreadPool()内部使用)、严格一对一协作(如握手协议) - 想实现“最多缓存 N 个任务”,必须用
LinkedBlockingQueue或ArrayBlockingQueue,哪怕 N=1 -
SynchronousQueue的isEmpty()永远返回true,size()永远是 0,不能用它判断“有没有待处理任务”
真正难的不是选哪个队列,而是想清楚你要的是“无损传递”“可控积压”还是“即时协同”——选错类型,后面怎么调参都救不回来。











