news 2026/5/2 17:39:48

Java并发跳表Map:无锁有序的高效实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java并发跳表Map:无锁有序的高效实现

一、整体定位:它是什么?

ConcurrentSkipListMap是一个可扩展、线程安全、按键排序的并发 Map,实现了ConcurrentNavigableMap接口。

  • 底层数据结构跳表(Skip List),而非红黑树(如TreeMap)。
  • 排序方式:支持自然排序(Comparable)或自定义Comparator
  • 并发模型无锁(Lock-Free),通过CAS(Compare-And-Swap)实现线程安全。
  • 作者:Doug Lea(Java 并发包之父)。
  • 引入版本:JDK 1.6。

适用场景
需要高并发读写 + 键有序 + 范围查询(如 subMap, headMap)的场景,例如:

  • 高频交易系统中的订单簿(按价格排序)
  • 缓存淘汰策略(如 LRU + 有序)
  • 分布式系统中的有序任务队列

二、为什么用“跳表”而不是“红黑树”?

这是 Doug Lea 在注释中明确解释的关键设计选择:

There are no known efficient lock-free insertion and deletion algorithms for search trees.

对比分析:

特性红黑树(TreeMap)跳表(ConcurrentSkipListMap)
单线程性能O(log n),常数因子小O(log n),常数因子略大
并发实现难度极难实现无锁(需复杂指针操作)天然适合无锁(链表 + 指针 CAS)
实现复杂度高(旋转、平衡)中(概率性结构,逻辑清晰)
内存局部性差(树节点分散)较好(链表连续,跳表索引缓存友好)
范围遍历支持支持,且正向更快

结论
高并发无锁场景下,跳表比平衡树更容易实现、更稳定、更可预测


三、核心并发机制:如何做到线程安全?

1.双层删除标记(Two-Phase Deletion)

这是最关键的无锁删除算法,避免 ABA 问题和中间状态不一致。

删除一个节点n(前驱b,后继f)分三步:
初始: ... → [b] → [n] → [f] → ... Step 1: 将 n.value 设为 null(逻辑删除) → 所有 get/containsKey 都认为 n 不存在 Step 2: 在 n 后插入一个 marker 节点 ... → [b] → [n] → [marker] → [f] → ... → 阻止其他线程在 n 后插入新节点 Step 3: CAS b.next 跳过 [n] 和 [marker] ... → [b] → [f] → ... → 物理删除完成,n 可被 GC

优势

  • 其他线程在遍历时若发现value == null,会主动“帮助”完成删除(helpDelete),避免阻塞。
  • 使用普通Node作为 marker(value == this),节省空间,避免AtomicMarkedReference的开销。

2.值为 null 表示逻辑删除

  • 正常节点:value是实际值(非 null)
  • 已删除节点:value = null
  • Marker 节点:value = this(自引用)

💡 这与Map.get(key)返回null表示“不存在”完美契合,无需额外标记。


3.索引层(Index Levels)独立维护

  • 基础层(base level):存储所有键值对,用上述无锁链表。
  • 索引层(index levels):多层“快速通道”,每层是基础层的子集。
  • 索引插入/删除是惰性的:先完成基础层操作,再异步更新索引。
    • 减少 CAS 冲突窗口
    • 避免索引节点内存泄漏(确保删除后索引不可达)

📌 跳表高度动态调整:插入时可能提升层级,删除时可能降低(启发式)。


4.弱一致性迭代器(Weakly Consistent Iterators)

  • 迭代器不会抛出ConcurrentModificationException
  • 能看到调用 iterator() 之前已存在的元素
  • 可能看到、也可能看不到并发修改的元素
  • 不会重复遍历同一元素

✅ 适合高并发读多写少场景,牺牲强一致性换取高性能。


四、重要注意事项(来自 Javadoc)

特性说明
size()不是 O(1)需遍历所有元素,结果可能是近似值(遍历时被修改)
不支持null键/值get(key)返回null表示“不存在”,无法区分
Entry.setValue()不支持返回的Map.Entry是快照,需用replace()修改
批量操作非原子putAll,clear,equals等不是原子操作
正向遍历更快升序(ascending)比降序(descending)性能更好

五、性能与空间

  • 时间复杂度get,put,remove,containsKey平均O(log n)
  • 空间开销:约 25% 节点有索引(p=0.5),总空间略小于TreeMap
  • 并发性能:远优于Collections.synchronizedMap(new TreeMap<>())

六、总结:如何理解这段代码?

ConcurrentSkipListMap是 Doug Lea 利用“跳表 + 无锁链表 + 双阶段删除 + 弱一致性”设计出的一个高性能并发有序 Map。它牺牲了部分强一致性(如 size() 不精确、迭代器弱一致),换来了极高的并发吞吐量和可扩展性,是高并发场景下替代TreeMap的首选。

关键创新点:

  1. 用普通 Node 实现删除标记,避免 AtomicMarkedReference 开销;
  2. 逻辑删除(value=null)与物理删除分离,支持高效并发;
  3. 索引层惰性更新,减少竞争,防止内存泄漏;
  4. 帮助机制(helpDelete),让其他线程协助完成删除,避免阻塞。

类比记忆:

想象一条高速公路(基础链表),上面每隔一段就有立交桥(索引层)。
当你要拆除一个出口(删除节点),先挂“施工中”牌子(value=null),
再建个临时隔离带(marker),最后把路直接接过去(CAS 跳过)。
其他司机(线程)看到施工牌,会主动帮你清理现场(helpDelete)!

这就是ConcurrentSkipListMap的智慧。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 23:11:12

深度测评9个AI论文写作软件,专科生毕业论文轻松搞定!

深度测评9个AI论文写作软件&#xff0c;专科生毕业论文轻松搞定&#xff01; AI 工具如何助力论文写作&#xff0c;专科生也能轻松应对 随着 AI 技术的不断进步&#xff0c;越来越多的学生开始借助 AI 工具来提升论文写作效率。尤其是在当前 AIGC&#xff08;人工智能生成内容&…

作者头像 李华
网站建设 2026/4/28 5:50:59

A/B测试框架搭建:对比不同prompt策略的效果差异

A/B测试框架搭建&#xff1a;对比不同prompt策略的效果差异 在当今AI工程实践中&#xff0c;一个越来越明显的趋势正在浮现——我们不再仅仅依赖模型本身的“大小”来判断其能力&#xff0c;而是更加关注如何通过精巧的交互设计去释放它的潜力。尤其是在轻量级专用模型逐渐崭露…

作者头像 李华
网站建设 2026/5/2 6:59:11

【Java毕设源码分享】基于springboot+vue的教通无界管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/5/1 14:25:10

亲测好用9个AI论文写作软件,专科生毕业论文轻松搞定!

亲测好用9个AI论文写作软件&#xff0c;专科生毕业论文轻松搞定&#xff01; AI 工具助力论文写作&#xff0c;专科生也能轻松应对 随着人工智能技术的不断发展&#xff0c;AI 写作工具逐渐成为学生群体&#xff0c;尤其是专科生在撰写毕业论文时的重要助手。这些工具不仅能够帮…

作者头像 李华
网站建设 2026/5/1 19:28:59

教育大模型的认知过载风险:个性化推荐与学习者自主性的矛盾

教育大模型的认知过载风险&#xff1a;个性化推荐与学习者自主性的矛盾 引言&#xff1a;智能教育的双刃剑 随着教育大模型&#xff08;Educational Large Language Models&#xff09;的快速发展&#xff0c;个性化学习推荐系统已成为智能教育的核心组件。这些系统通过分析学习…

作者头像 李华
网站建设 2026/4/23 15:12:53

unique_lock<mutex> uLock 的用法

std::unique_lock<std::mutex> 是 C11 提供的一种更灵活的互斥锁管理器&#xff0c;常用于多线程同步场景。std::unique_lock<std::mutex> uLock; 本质上是一个可控制加锁/解锁时机、可转移所有权的锁对象。一、unique_lock 是干什么的&#xff1f;一句话总结&…

作者头像 李华