核心速览

方案 示例 锁粒度 性能 适用场景
并发容器 ConcurrentHashMap 细粒度 / 无锁 高并发读写
同步包装器 Collections.synchronizedMap() 全局锁 低并发、简单场景
传统同步集合 Hashtable, Vector 全局锁 兼容旧代码

常见误区

  1. ConcurrentHashMap.size() 是实时精确的。
    JDK 8+ 中 size() 是精确的(通过遍历所有桶计数),但高并发写时调用 size() 仍可能有短暂不一致,且性能开销大。建议:避免频繁调用 size(),或使用 mappingCount()(返回 long,更适用于大容量)。

  2. ConcurrentHashMap 允许 null。
    ConcurrentHashMap 不允许 null,put(key, null) 会抛出 NullPointerException,这主要是为了避免二义性(get(key) == null 无法区分“不存在”还是“值为 null”)。

  3. ConcurrentLinkedQueuesize() 方法?
    ConcurrentLinkedQueue.size() 方法的时间复杂度是 O(n),因为它是无锁链表,需遍历计数。非阻塞队列应避免提供 size(),因其在并发下意义不大。


并发容器是 java.util.concurrent 包中提供的一组线程安全的集合类,它们在多线程环境下可以安全地被多个线程并发读写,而无需外部加锁。相比对 VectorHashtableCollections.synchronizedXxx() 的全局锁实现,并发容器通过更细粒度的锁(如分段锁)或无锁算法(如 CAS)大幅提升了并发性能。

它们的目标是:

  1. 允许多个线程同时读。
  2. 允许读与写在某些条件下并发进行。
  3. 在保证线程安全的前提下最大化吞吐量。

并发 Map

底层结构 线程安全机制 特点
ConcurrentHashMap 分段哈希表 (JDK 7)
CAS + synchronized锁桶 (JDK 8+)
无锁读 + 细粒度写锁 - 高并发读写首选
- 不允许 null key/value
- 支持原子复合操作
ConcurrentSkipListMap 跳表 (Skip List) CAS + volatile - 有序 Map
- 并发性能优于 TreeMap

1、ConcurrentHashMap 原理演进 (JDK 8+)

  • JDK 7:使用分段锁(Segment),将哈希表分成 16 段,每段独立加锁。
  • JDK 8+:取消 Segment,直接对每个哈希桶(bin)的头节点加 synchronized 锁。
    • 读操作完全无锁(依赖 volatile 字段保证可见性)。
    • 写操作(如 put)只锁住目标桶,其他桶可并发访问。
    • 高并发下性能显著优于 HashtablesynchronizedMap

2、原子复合操作 (JDK 8+)

以下方法内部已处理竞争,无需外部同步。

// 线程安全:若 key 不存在,则计算并插入
map.computeIfAbsent("key", k -> expensiveComputation());

// 线程安全:仅当值等于 oldValue 时才更新
map.replace("key", oldValue, newValue);

// 线程安全:累加
map.computeIfPresent("counter", (k, v) -> v + 1);

并发 Queue / Deque

特性 适用场景
ConcurrentLinkedQueue 无界、非阻塞、FIFO 高吞吐、无界任务队列
ConcurrentLinkedDeque 无界、非阻塞、双端队列 工作窃取(Work-Stealing)等
LinkedBlockingQueue 有界/无界、阻塞、FIFO 线程池任务队列(如 Executors.newFixedThreadPool
ArrayBlockingQueue 有界、阻塞、FIFO 固定容量缓冲区
SynchronousQueue 无容量、直接传递 生产者-消费者直接交接(如 Executors.newCachedThreadPool
PriorityBlockingQueue 无界、阻塞、优先级队列 按优先级处理任务

⚠️ “并发” ≠ “阻塞”

  • ConcurrentLinkedQueue 是非阻塞的(失败即返回,不等待)。
  • BlockingQueue 实现是阻塞的(如 put() 在队列满时会等待)。

并发 Set

Java 没有直接提供 ConcurrentHashSet,但可以通过 ConcurrentHashMap.newKeySet() 来创建。其内部基于 ConcurrentHashMap(key 为元素,value 为 Boolean.TRUE),因此拥有 ConcurrentHashMap 所有的并发优势。

Copy-On-Write 容器

机制 适用场景 特点
CopyOnWriteArrayList 写时复制(每次修改都创建新数组) 读多写少(如监听器列表) - 读操作无锁、高性能。
- 写操作昂贵(需复制整个数组)。
- 弱一致性:读线程可能看不到最新写入。
CopyOnWriteArraySet 基于 CopyOnWriteArrayList 读多写少(如监听器列表) 同上

使用 CopyOnWriteArrayList 注意事项:

  1. 仅适用于写操作非常少的场景,且能容忍读写的短暂不一致。
  2. 迭代器是只读的,不支持增删改。因为它遍历的是一个快照。

最佳实践

  1. Map 场景首选 ConcurrentHashMap
  2. 队列场景,如果需要阻塞使用 BlockingQueue(如 LinkedBlockingQueue),如果追求极致吞吐且无界可以使用 ConcurrentLinkedQueue
  3. Set 场景可以通过 ConcurrentHashMap.newKeySet() 来构造。
  4. 读多写少的静态数据场景可以选择使用 CopyOnWriteArrayList
  5. 尽量使用并发容器自带的复合操作 API(如 ConcurrentHashMap.putIfAbsent),不要自行操作(如 if (!set.contains(x)) set.add(x))。

JDK 21 中的新特性与演进

  1. ConcurrentHashMap 在 JDK 16+ 进一步优化了并发度和内存布局。
  2. 虚拟线程(Virtual Threads):所有并发容器均可安全用于虚拟线程环境,无特殊适配需求。
  3. JDK 15+ 中 JVM 移除偏向锁,对 ConcurrentHashMapsynchronized 桶锁影响极小,因其锁竞争本就低。

一键三连,让我的信心像气球一样膨胀!

更多推荐