这是关于 JDKjava.util.concurrent包下并发集合Concurrent Collections的设计原理、核心机制与实战教程。内容会从设计哲学深入到具体实现并附带代码示例。第一部分设计哲学与核心概念并发集合的设计目标是在多线程环境下提供比synchronized包装类如Collections.synchronizedMap更高的并发性。1.1 核心设计原则细粒度锁Fine-Grained Locking不对整个数据结构加锁而是对数据的一部分如桶、节点加锁允许不同线程操作不同部分。无锁Lock-Free与乐观锁利用 CASCompare-And-SwapCPU 原子指令避免线程阻塞如ConcurrentLinkedQueue。写时复制Copy-On-Write修改时复制一份新数据读操作永远无需加锁如CopyOnWriteArrayList。弱一致性Weakly Consistent迭代器迭代器不抛出ConcurrentModificationException且能反映迭代开始时的部分状态但未必是最新状态。1.2 与遗留同步容器的对比特性同步容器Hashtable, Vector并发集合ConcurrentHashMap锁粒度整个对象锁分段锁 / 节点锁并发度低同一时刻仅一个线程操作高多线程可并发读写不同段迭代器快速失败Fail-Fast弱一致不抛异常null键/值不允许HashtableConcurrentHashMap不允许null第二部分核心并发集合工作原理详解2.1 ConcurrentHashMap重点设计演进JDK 1.7分段锁Segment 数组每个 Segment 继承 ReentrantLock。JDK 1.8废除分段锁采用CAS synchronized锁定链表/红黑树的头节点。核心机制初始化延迟初始化首次put时通过 CAS 创建数组。插入put流程若桶为空CAS 尝试插入新节点。若桶不为空synchronized锁定桶的头节点进行链表遍历或红黑树插入。扩容transfer支持多线程并发扩容每个线程负责一部分桶的迁移。使用sizeCtl控制状态负数表示正在扩容。计数size/mappingCount使用 LongAdder 风格的CounterCell数组分散计数减少竞争。注意ConcurrentHashMap不允许 key/value 为 null因为这会混淆“未找到”和“值为 null”的场景在并发下无法用containsKey准确判断。2.2 BlockingQueue 实现类生产者-消费者模型ArrayBlockingQueue有界内部使用单锁ReentrantLock控制入队出队支持公平/非公平。LinkedBlockingQueue可选有界内部使用双锁takeLock putLock分离读写吞吐量更高。SynchronousQueue无容量每个插入必须等待一个提取常用于线程池Executors.newCachedThreadPool。PriorityBlockingQueue无界基于优先堆元素需实现 Comparable。工作原理利用ConditionnotEmpty/notFull实现阻塞等待。当队列满时put线程阻塞当队列空时take线程阻塞。2.3 CopyOnWriteArrayList / CopyOnWriteArraySet适用场景读多写极少如黑名单、监听器列表。原理执行写操作add/remove/set时复制底层数组修改后重新赋值给 volatile 引用。优点读操作无需锁性能极高。缺点写操作开销大复制成本数据强一致性延迟高。2.4 ConcurrentLinkedQueue无界非阻塞队列基于Michael-Scott 无锁算法。核心使用 CAS 更新头尾节点head和tail不一定指向真正的首尾存在滞后性。迭代器弱一致性遍历时若节点被删除跳过即可。2.5 ConcurrentSkipListMap / SkipListSet有序并发集合基于跳表Skip List结构。优势提供 O(log n) 的并发访问避免红黑树复杂的重平衡操作。用途需要并发有序 Map 时如替代TreeMapCollections.synchronized。第三部分实战教程与代码示例3.1 高性能缓存场景使用 ConcurrentHashMap模拟一个简单的线程安全缓存支持超时失效简化版。import java.util.concurrent.*; public class ConcurrentCacheK, V { private final ConcurrentHashMapK, FutureV cache new ConcurrentHashMap(); private final ConcurrentHashMapK, Long expireMap new ConcurrentHashMap(); private final long ttlMillis; public ConcurrentCache(long ttlMillis) { this.ttlMillis ttlMillis; } public V get(K key, CallableV loader) throws Exception { long now System.currentTimeMillis(); // 检查过期 Long expireTime expireMap.get(key); if (expireTime ! null now expireTime) { cache.remove(key); expireMap.remove(key); } FutureV future cache.get(key); if (future null) { FutureTaskV task new FutureTask(loader); FutureV existing cache.putIfAbsent(key, task); if (existing null) { future task; task.run(); // 执行加载 expireMap.put(key, now ttlMillis); } else { future existing; } } return future.get(); } }3.2 生产者-消费者使用 LinkedBlockingQueuepublic class ProducerConsumerDemo { public static void main(String[] args) { BlockingQueueInteger queue new LinkedBlockingQueue(10); Runnable producer () - { try { for (int i 0; i 20; i) { queue.put(i); // 阻塞直到有空间 System.out.println(Produced: i); } } catch (InterruptedException ignored) {} }; Runnable consumer () - { try { while (true) { Integer val queue.take(); // 阻塞直到有元素 System.out.println(Consumed: val); Thread.sleep(100); } } catch (InterruptedException ignored) {} }; new Thread(producer).start(); new Thread(consumer).start(); new Thread(consumer).start(); // 多消费者 } }3.3 读多写少场景使用 CopyOnWriteArrayListpublic class BlacklistService { // 黑名单列表读操作远多于写操作 private final CopyOnWriteArrayListString blacklist new CopyOnWriteArrayList(); public boolean isBlacklisted(String ip) { return blacklist.contains(ip); // 无锁极快 } public void addToBlacklist(String ip) { blacklist.add(ip); // 复制数组开销大 } public void removeFromBlacklist(String ip) { blacklist.remove(ip); } }第四部分常见面试题与调优建议4.1 常见面试问题Q1ConcurrentHashMap 在 JDK 1.8 中为什么放弃分段锁分段锁占用内存多每个 Segment 继承 ReentrantLock。分段锁在扩容时仅锁住当前 Segment但全局统计 size 需要获取所有锁效率不高。使用 CAS synchronized 锁头节点锁粒度更细仅锁住单个桶支持更高并发。Q2ConcurrentHashMap 如何保证线程安全初始化数组CAS 保证单次初始化。写操作CAS 插入空桶非空桶用 synchronized 锁头节点。扩容多线程协同迁移每个线程使用 CAS 抢占任务。Q3ConcurrentLinkedQueue 的 size() 方法为何不准确因为在并发环境下size 需要遍历全队列且遍历过程中队列可能变化因此返回的是弱一致性的近似值。4.2 性能调优建议场景推荐集合注意事项高频读写 MapConcurrentHashMap初始容量估算准确避免频繁扩容。生产者-消费者LinkedBlockingQueue若消费者慢于生产者防止 OOM设置边界。大量读、极少写 ListCopyOnWriteArrayList避免频繁写入否则复制成本巨大。并发排序/范围查询ConcurrentSkipListMap比 ConcurrentHashMap 有序但吞吐量略低。高并发计数器LongAdder非集合比AtomicLong更适合累积统计。4.3 常见陷阱误用ConcurrentHashMap复合操作非原子if (!map.containsKey(k)) map.put(k,v)不是原子操作应改用putIfAbsent或computeIfAbsent。阻塞队列的drainTo()在批量取数据时效率远高于循环take()。不要将null值放入ConcurrentHashMap否则可能 NPE。第五部分总结思维导图文字版JDK 并发集合 ├── 设计哲学 │ ├── 细粒度锁 │ ├── CAS无锁 │ ├── Copy-On-Write │ └── 弱一致性迭代器 ├── Map体系 │ ├── ConcurrentHashMapCASsynchronized无序 │ └── ConcurrentSkipListMap跳表有序 ├── List/Set体系 │ ├── CopyOnWriteArrayList/Set读多写少 │ └── 无ConcurrentArrayList自行Collections.synchronized ├── Queue/Deque体系 │ ├── ConcurrentLinkedQueue无界非阻塞 │ ├── LinkedBlockingQueue有界双锁阻塞 │ ├── ArrayBlockingQueue有界单锁阻塞 │ └── SynchronousQueue0容量传递 └── 工具类辅助 ├── LongAdder高并发计数 └── CompletableFuture异步编排