连载中 17/21

并发容器补全:CopyOnWriteArrayList 与 BlockingQueue 家族

2026-10-31 · 29 阅读 · 0 评论 · 0 赞

CopyOnWriteArrayList:写时复制

读操作完全无锁——直接读 volatile 数组;写操作加锁后复制整个数组、在副本上改、再整块换引用。读写不冲突,读读更不冲突。代价也明摆着:写一次复制一次,写多就是灾难;内存瞬间翻倍;迭代器是创建那一刻的快照,弱一致——迭代中别人写的,你看不见。所以它的适用面极窄但极准:读多写极少——监听器列表、白名单、配置快照。典型用法:事件监听器注册一次跑一年,每次事件触发遍历全表,COW 让遍历零开销。

BlockingQueue 家族:锁粒度的又一次演进

阻塞队列是生产者消费者的标准答案——满了 put 自动等、空了 take 自动等,手写 wait/notify 的活它全包了。几个成员的锁设计各有心思:ArrayBlockingQueue——有界数组,一把锁管读写,简单但读写互斥;LinkedBlockingQueue——链表结构,两把锁分离读写(putLock/takeLock),读写可以并行,吞吐更高(线程池的无界队列用的就是它);SynchronousQueue——容量为零,不存,put 必须等到 take 来接手才算完,直接交接(cachedThreadPool 用的它);PriorityBlockingQueue——按优先级出队;DelayQueue——到时间才能取,延迟任务的底座(定时线程池用的类似结构)。

三组 API,别混着用

每个队列都提供三组行为,语义差之毫厘谬以千里:阻塞组——put/take,满了或空了就等,最常用;让路组——offer/poll,不行就返回 false/null,可带超时,配合「试一下不行就撤」的业务逻辑;炸雷组——add/remove,满了或空了直接抛异常。新手常把 add 当 put 用,队列一满就爆炸。写生产消费代码,默认用阻塞组,边界场景用让路组,炸雷组基本别碰。

现代版生产者消费者

对比第 4 篇手写的 wait/notify 缓冲区,BlockingQueue 版本三行完事:

BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

// 生产者线程
queue.put(produce());

// 消费者线程
Integer item = queue.take();

没有 wait、没有 while、没有 notifyAll——第 4 篇讲的那些铁律,全被封装进了队列内部。自己手写 wait/notify 的意义在于理解原理,生产代码请直接用队列。顺带一提:不需要阻塞语义的并发队列还有 ConcurrentLinkedQueue(无锁 CAS 实现),性能更高但 API 不阻塞,按需选用。

容器篇完。下一篇协作三件套:CountDownLatch、CyclicBarrier、Semaphore——不是锁,是让线程「等齐」与「限流」的工具。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#Java#CopyOnWriteArrayList#BlockingQueue#并发容器#JUC

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10113 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9876 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9306 阅读 · 21 评论 · 287 赞