)
第50篇阻塞队列与并发容器2026版系列导航《Java 100 天进阶之路》完整目录 |⬅️ 上一篇第49篇ConcurrentHashMap原理 |➡️ 下一篇第51篇线程生命周期与创建方式文章目录第50篇阻塞队列与并发容器2026版️ 本文阅读地图3 分钟速览一、核心知识点二、生活类比从“奶茶店柜台”到“智能餐厅”三、阻塞队列核心机制3.1 什么是 BlockingQueue3.2 核心 API 速记3.3 五大实现类对比3.4 源码片段ArrayBlockingQueue 的 put/take锁条件3.5 源码片段LinkedBlockingQueue 的 put/take双锁设计四、CopyOnWriteArrayList读多写少的并发 List4.1 核心思想4.2 源码片段4.3 优缺点与适用场景五、ConcurrentLinkedQueueCAS 无锁非阻塞队列5.1 核心思想5.2 核心特点5.3 核心方法六、线程池中的阻塞队列选型七、生产级避坑清单八、面试高频考点面试官追问陷阱加分题九、练习题 你的学习进度 下一篇文章预告️ 本文阅读地图3 分钟速览第45~49篇拿下了 HashMap 和 ConcurrentHashMap本篇是集合框架源码系列收官之作聚焦并发场景下的队列与 List 容器模块核心问题一句话回答BlockingQueue 是什么阻塞队列解决了什么问题生产者-消费者模型的线程安全桥梁队列满/空时自动阻塞Array vs Linked vs Sync三种阻塞队列怎么选固定容量用 Array高吞吐用 Linked线程池传引用用 SyncCopyOnWriteArrayList并发读多写少用什么 List读无锁、写时复制读多写少场景的王者ConcurrentLinkedQueue非阻塞队列是什么CAS 无锁实现适合高并发、不要求阻塞的场景线程池中的应用阻塞队列在线程池里起什么作用任务缓冲核心参数之一一、核心知识点阻塞队列BlockingQueue定义支持阻塞入队/出队的线程安全队列队列满时put()阻塞生产者队列空时take()阻塞消费者五大实现ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue、DelayQueue核心 APIput(e)/take()阻塞、offer(e)/poll()非阻塞、offer(e, timeout)/poll(timeout)超时并发容器CopyOnWriteArrayList写时复制读无锁写加锁读多写少场景首选ConcurrentLinkedQueueCAS 无锁非阻塞队列高并发任务队列首选二、生活类比从“奶茶店柜台”到“智能餐厅”BlockingQueue 就像一个奶茶店柜台柜台容量有限有界队列做好的奶茶放在柜台上。顾客消费者来取柜台空了就等着take()阻塞。店员生产者做好了放上去柜台满了就等一下再放put()阻塞。SynchronousQueue 是“手递手”柜台柜台没有放置空间店员做好一杯奶茶必须直接递到顾客手里两边同时在场才能完成交接。CopyOnWriteArrayList 像“复印店”多人同时阅读一份报纸读操作无锁。有人要改内容时先复印一份新报纸在副本上改改完再替换原版写时复制。正在读旧版的人不受影响。三、阻塞队列核心机制3.1 什么是 BlockingQueueBlockingQueue是java.util.concurrent包下的线程安全队列接口专为生产者-消费者模型设计。核心特性线程安全所有实现类保证多线程并发操作的安全性阻塞插入队列满时put(e)阻塞生产者线程阻塞移除队列空时take()阻塞消费者线程超时支持offer(e, timeout, unit)和poll(timeout, unit)支持超时阻塞容量限制分为有界队列固定容量和无界队列理论无限容量3.2 核心 API 速记操作类型队列满/空时行为插入方法移除方法检查方法抛出异常立即抛异常add(e)remove()element()返回特殊值返回 false/nulloffer(e)poll()peek()阻塞阻塞直到成功put(e)take()—超时超时返回 false/nulloffer(e, time, unit)poll(time, unit)—3.3 五大实现类对比实现类底层结构容量核心特点适用场景ArrayBlockingQueue数组有界必须指定公平锁可选内存紧凑固定容量生产者-消费者LinkedBlockingQueue链表可选有界默认无界生产者和消费者用独立锁吞吐量高高吞吐任务队列SynchronousQueue无存储容量为 0手递手生产等消费线程池CachedThreadPoolPriorityBlockingQueue堆数组无界按优先级排序优先级任务调度DelayQueue堆 延迟无界元素需实现Delayed到期才能取出定时任务、订单超时关闭 关键区别Array vs LinkedArray 用一把锁Linked 用两把锁takeLockputLock吞吐量更高默认容量new LinkedBlockingQueue()是无界Integer.MAX_VALUE极易 OOMSynchronousQueue容量为 0不存储元素生产者直接等待消费者接收3.4 源码片段ArrayBlockingQueue 的 put/take锁条件// ArrayBlockingQueue 核心一把锁 两个条件publicclassArrayBlockingQueueE{finalReentrantLocklock;// 唯一锁privatefinalConditionnotEmpty;// 队列非空条件privatefinalConditionnotFull;// 队列未满条件publicvoidput(Ee)throwsInterruptedException{lock.lockInterruptibly();try{while(countitems.length)// 队列满 → 等待notFull.await();enqueue(e);}finally{lock.unlock();}}publicEtake()throwsInterruptedException{lock.lockInterruptibly();try{while(count0)// 队列空 → 等待notEmpty.await();returndequeue();}finally{lock.unlock();}}}3.5 源码片段LinkedBlockingQueue 的 put/take双锁设计// LinkedBlockingQueue 核心两把锁分离生产与消费publicclassLinkedBlockingQueueE{privatefinalReentrantLocktakeLocknewReentrantLock();// 消费者锁privatefinalReentrantLockputLocknewReentrantLock();// 生产者锁privatefinalConditionnotEmptytakeLock.newCondition();privatefinalConditionnotFullputLock.newCondition();publicvoidput(Ee)throwsInterruptedException{putLock.lockInterruptibly();// 只锁生产者try{while(countcapacity)notFull.await();enqueue(e);}finally{putLock.unlock();}}publicEtake()throwsInterruptedException{takeLock.lockInterruptibly();// 只锁消费者try{while(count0)notEmpty.await();returndequeue();}finally{takeLock.unlock();}}}双锁设计生产者和消费者可同时操作互不阻塞吞吐量更高。四、CopyOnWriteArrayList读多写少的并发 List4.1 核心思想CopyOnWriteArrayList是线程安全的ArrayList变体核心思想是写时复制Copy-On-Write写操作加锁复制一份新数组修改完成后替换原数组读操作完全无锁直接读当前数组4.2 源码片段publicclassCopyOnWriteArrayListE{privatetransientvolatileObject[]array;// volatile 保证可见性// 读操作无锁publicEget(intindex){returnget(getArray(),index);// 直接读不加锁}// 写操作加锁 复制publicbooleanadd(Ee){finalReentrantLocklockthis.lock;lock.lock();// 1. 加锁try{Object[]elementsgetArray();intlenelements.length;Object[]newElementsArrays.copyOf(elements,len1);// 2. 复制newElements[len]e;// 3. 修改副本setArray(newElements);// 4. 替换引用returntrue;}finally{lock.unlock();}}}4.3 优缺点与适用场景维度说明✅ 优点读操作无锁读多写少场景性能极高迭代器不抛ConcurrentModificationException❌ 缺点每次写都复制整个数组写操作代价高数据弱一致性读可能读到旧数据适用场景读多写少配置列表、黑白名单、缓存数据等不适用场景写操作频繁的场景如实时计数器五、ConcurrentLinkedQueueCAS 无锁非阻塞队列5.1 核心思想ConcurrentLinkedQueue是无界、非阻塞、线程安全的 FIFO 队列基于链表 CAS实现。与 BlockingQueue 的本质区别BlockingQueue用锁实现阻塞put/take可阻塞线程ConcurrentLinkedQueue用CAS实现非阻塞offer/poll立即返回不阻塞5.2 核心特点特性说明线程安全多线程并发插入/删除不会导致数据不一致非阻塞使用 CAS 操作避免锁竞争导致的线程阻塞无界可动态扩展理论无限容量受内存限制高性能无锁设计高并发下性能优于阻塞队列5.3 核心方法ConcurrentLinkedQueueStringqueuenewConcurrentLinkedQueue();queue.offer(task1);// 插入立即返回 truequeue.add(task2);// 插入失败抛异常Stringtaskqueue.poll();// 移除并返回头元素空返回 nullStringtaskqueue.peek();// 返回头元素不移除空返回 null六、线程池中的阻塞队列选型线程池默认阻塞队列特点FixedThreadPoolLinkedBlockingQueue无界队列任务量需可控否则 OOMCachedThreadPoolSynchronousQueue不存任务直接交给线程无任务时销毁线程SingleThreadExecutorLinkedBlockingQueue同 Fixed单线程ScheduledThreadPoolDelayedWorkQueue延迟队列支持定时/周期任务七、生产级避坑清单✅ 阻塞队列与并发容器使用规范 1. LinkedBlockingQueue 无参构造默认无界Integer.MAX_VALUE→ 务必指定容量否则 OOM 2. ArrayBlockingQueue 必须指定容量创建时明确队列大小 3. SynchronousQueue 不存储元素offer() 无消费者立即返回 false必须用 put() 4. CopyOnWriteArrayList 写操作频繁时性能极差 → 仅用于读多写少场景 5. ConcurrentLinkedQueue 的 size() 是 O(n) 遍历 → 不要频繁调用 6. 阻塞队列的 take() 会阻塞线程 → 确保有对应的生产者否则线程永久阻塞八、面试高频考点Q1BlockingQueue 的核心方法有哪些区别是什么四类方法① 抛异常add/remove/element② 返回特殊值offer/poll/peek③ 阻塞put/take④ 超时offer(time)/poll(time)。put/take是阻塞队列的核心方法。Q2ArrayBlockingQueue 和 LinkedBlockingQueue 的区别Array 基于数组必须指定容量用一把锁内存紧凑。Linked 基于链表可选容量默认无界生产者和消费者用独立锁吞吐量更高。高并发场景 Linked 更优但需注意无界队列可能 OOM。Q3SynchronousQueue 的作用容量为 0 的阻塞队列不存储元素生产者插入必须等待消费者接收。常用于Executors.newCachedThreadPool()实现任务直接传递给线程。Q4CopyOnWriteArrayList 的原理和适用场景写时复制写操作加锁复制整个数组修改后替换原数组读操作完全无锁。适用于读多写少场景如配置列表、黑白名单。写频繁时性能极差。Q5ConcurrentLinkedQueue 和 BlockingQueue 的区别ConcurrentLinkedQueue是非阻塞队列基于 CAS 无锁实现BlockingQueue是阻塞队列基于锁 Condition 实现。前者适合高并发任务队列后者适合生产者-消费者模型。面试官追问陷阱加分题追问1“new LinkedBlockingQueue()不指定容量会怎样” 默认容量是Integer.MAX_VALUE近似无界。如果生产者速度持续快于消费者队列会无限膨胀最终OOM。生产环境务必指定容量。追问2“CopyOnWriteArrayList 的迭代器会抛ConcurrentModificationException吗”不会。迭代器基于创建时的数组快照遍历期间不感知后续修改因此不会抛异常。代价是读不到最新数据弱一致性。追问3“ConcurrentLinkedQueue的size()为什么不准确”size()需要遍历整个链表累加计数时间复杂度 O(n)且遍历过程中可能有并发修改结果不精确。高并发场景下慎用。九、练习题源码推导ArrayBlockingQueue和LinkedBlockingQueue的锁机制有什么区别各有什么优缺点场景设计某系统需要维护一份读频率极高、几乎不修改的敏感词列表选用什么并发容器最合适代码分析下面的代码有什么问题LinkedBlockingQueueStringqueuenewLinkedBlockingQueue();for(inti0;i1000000;i){queue.put(taski);} 你的学习进度当前第50篇 / 共108篇 ·进阶篇集合框架源码解析第45~50篇✅ 已完成基础篇44篇 第45~50篇集合框架源码系列收官 正在学第50篇⏳ 待学习第51~108篇 完整目录 学习指南 | 订阅本专栏不错过每一篇 下一篇文章预告下一篇《第51篇线程生命周期与创建方式》内容简介线程的 6 种状态NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED、状态流转图、创建线程的 4 种方式Thread、Runnable、Callable、线程池。第45~50篇集合框架源码系列正式收官下一阶段开启多线程与高并发专题《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇建议收藏 关注一起100天拿offer 点击关注我更新后第一时间收到推送