JDK源码中的跳表实现类: ConcurrentSkipListMap和ConcurrentSkipListSet。其中ConcurrentSkipListSet的实现是基于ConcurrentSkipListMap。因此下面具体分析ConcurrentSkipListMap的实现:
//查找指定Key的前置节点 private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) { if (key == null) throw new NullPointerException(); // don't postpone errors for (;;) { for (Index<K,V> q = head, r = q.right, d;;) { if (r != null) { Node<K,V> n = r.node; K k = n.key; if (n.value == null) { if (!q.unlink(r)) // 如果节点r=q.right为空,则删除该节点r,即把节点q.right指向r.right. break; // restart 然后跳出本次循环,从头节点开始继续循环。 r = q.right; // reread r continue; } if (cpr(cmp, key, k) > 0) {// 通过Key值于当前节点的right节点比较,如果Key值较大,则继续往右比较 q = r; r = r.right; continue; } } if ((d = q.down) == null) // 如果当前节点的down为空,则当前链表为最底层链表,该节点的值<=key,此即为查询结果。 return q.node; q = d; // 如果Key值不比当前节点的right节点大,则继续往下比较 r = d.right; } } } // 根据Key查找对应的节点 private Node<K,V> findNode(Object key) { if (key == null) throw new NullPointerException(); // don't postpone errors Comparator<? super K> cmp = comparator; outer: for (;;) { for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {//从key的前置节点开始查找 Object v; int c; if (n == null) break outer; Node<K,V> f = n.next; if (n != b.next) // inconsistent read 读写不一致,重新开始查找 break; if ((v = n.value) == null) { // n is deleted 下一个节点为null,则删除该节点,重新开始查找 n.helpDelete(b, f); break; } if (b.value == null || v == n) // b is deleted break; if ((c = cpr(cmp, key, n.key)) == 0) //查找到,则返回结果 return n; if (c < 0) break outer; b = n; n = f; } } return null; } private V doGet(Object key) 方法的实现于findNode一致,只是返回值为Value的复制。 /** * * Main insertion method. Adds element if not present, or * replaces value if present and onlyIfAbsent is false. * @param key the key * @param value the value that must be associated with key * @param onlyIfAbsent if should not insert if already present * @return the old value, or null if newly inserted * 新增一个节点过程: * 1,根据新增的节点key值,寻找其合适的插入位置b; * 2,如果存在相等的key值,则根据onlyIfAbsent决定是否更新对应的Value值,然后返回; * 3,如果不存在相等的key值,则创建一个新的节点,并插入到合适的位置b,此时操作的是跳表的最底层; * 4,根据随机函数,决定是否添加上层节点,如果不需要添加,则直接返回null; * 5,如果需要添加上层节点,则获取随机值level; * 6,如果随机值level不大于当前最大层数,则创建一个从第一层到第level层的新的节点Index链表,其通过down指针连接,right指针都设置为null; * 7,如果随机值level大于当前最大层数,则跳表的最大层数加1,然后创建一个从第一层到新的最大层的新的节点Index链表,其通过down指针连接,right指针都设置 * 为null;然后从旧的最大层数+1到新的最大层数间新增head节点链表,其通过down指针连接,right指针指向刚新增的对应层的Index节点; * 8,从旧的最大层数开始往最底层,把新增的index节点插入到合适的位置,即更新其right指针(完善第6步的操作)。至此,完成新增节点的整个过程。 */ private V doPut(K key, V value, boolean onlyIfAbsent) { Node<K,V> z; // added node if (key == null) throw new NullPointerException(); Comparator<? super K> cmp = comparator; outer: for (;;) { for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) {// 根据Key找到前置节点,然后开始查找 if (n != null) { Object v; int c; Node<K,V> f = n.next; if (n != b.next) // inconsistent read break; if ((v = n.value) == null) { // n is deleted n.helpDelete(b, f); break; } if (b.value == null || v == n) // b is deleted break; if ((c = cpr(cmp, key, n.key)) > 0) { //继续往右查找 b = n; n = f; continue; } if (c == 0) { if (onlyIfAbsent || n.casValue(v, value)) {//如果存在key相等的节点,则如果onlyIfAbsent=false,则通过casValue更新Key对应的Value值。如果onlyIfAbsent=true,则不更新Key对应的Value值,然后返回oldValue。 @SuppressWarnings("unchecked") V vv = (V)v; return vv; } break; // restart if lost race to replace value } // else c < 0; fall through } z = new Node<K,V>(key, value, n); // 没有查找到对应的Key节点,则新增一个节点 if (!b.casNext(n, z)) // 把新增的节点z设为当前节点的next节点;原子操作,失败则不断的循环操作 break; // restart if lost race to append to b break outer; } } int rnd = ThreadLocalRandom.nextSecondarySeed(); if ((rnd & 0x80000001) == 0) { // test highest and lowest bits //8000000001 = 1000 0000 0000 0000 0000 0000 0000 0001 测试最高位和最低位是否为0 int level = 1, max; while (((rnd >>>= 1) & 1) != 0) //无符号右移1位, 随机获得level值 ++level; Index<K,V> idx = null; HeadIndex<K,V> h = head; if (level <= (max = h.level)) { for (int i = 1; i <= level; ++i) idx = new Index<K,V>(z, idx, null); } else { // try to grow by one level 使整个跳表的level增长1 level = max + 1; // hold in array and later pick the one to use @SuppressWarnings("unchecked")Index<K,V>[] idxs = (Index<K,V>[])new Index<?,?>[level+1]; for (int i = 1; i <= level; ++i) idxs[i] = idx = new Index<K,V>(z, idx, null); //idx = new Index<K,V>(z,idx,null); 设置idx值,并设置其down和right值 //idxs[i] = idx; 设置每一层中新增的Index节点,其right值都设为null,down值设置为其下一层的Index节点。 for (;;) { h = head; int oldLevel = h.level; if (level <= oldLevel) // lost race to add level break; HeadIndex<K,V> newh = h; Node<K,V> oldbase = h.node; for (int j = oldLevel+1; j <= level; ++j) //设置新增的Head节点,设置其node,down,right和level值 newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j); if (casHead(h, newh)) { //更新head值成功,则退出无限循环 h = newh; //h 为新的跳表的head节点 idx = idxs[level = oldLevel]; //新增的层中,包含Head和idxs[max]两个节点,其指向关系已经确定,而oldLevel中,还没有设置idxs[level]的前置节点,因此idx = idxs[level = oldLevel],说明需要从此层开始至最底层,设置好idxs[level]的前置节点,下面的代码splice完成该功能。 break; } } } // find insertion points and splice in splice: for (int insertionLevel = level;;) { int j = h.level; for (Index<K,V> q = h, r = q.right, t = idx;;) { if (q == null || t == null) break splice; if (r != null) { Node<K,V> n = r.node; // compare before deletion check avoids needing recheck int c = cpr(cmp, key, n.key);//key 根当前node.key比较 if (n.value == null) { if (!q.unlink(r)) break; r = q.right; continue; } if (c > 0) { //继续往右查找 q = r; r = r.right; continue; } } if (j == insertionLevel) { if (!q.link(r, t)) // 把节点t插入到q和r之间,t即新增的节点idx[level] break; // restart if (t.node.value == null) { findNode(key); break splice; } if (--insertionLevel == 0)//层数往下,如果已到最底层,则退出,最底层的节点值在之前的代码中已经完成插入。 break splice; } if (--j >= insertionLevel && j < level) t = t.down; //t值更新,t即新增的节点idx[level] q = q.down; r = q.right; } } } return null; } /** * Main deletion method. Locates node, nulls value, appends a * deletion marker, unlinks predecessor, removes associated index * nodes, and possibly reduces head index level. * * Index nodes are cleared out simply by calling findPredecessor. * which unlinks indexes to deleted nodes found along path to key, * which will include the indexes to this node. This is done * unconditionally. We can't check beforehand whether there are * index nodes because it might be the case that some or all * indexes hadn't been inserted yet for this node during initial * search for it, and we'd like to ensure lack of garbage * retention, so must call to be sure. * * @param key the key * @param value if non-null, the value that must be * associated with key * @return the node, or null if not found */ final V doRemove(Object key, Object value) { if (key == null) throw new NullPointerException(); Comparator<? super K> cmp = comparator; outer: for (;;) { for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) { Object v; int c; if (n == null) break outer; Node<K,V> f = n.next; if (n != b.next) // inconsistent read break; if ((v = n.value) == null) { // n is deleted n.helpDelete(b, f); break; } if (b.value == null || v == n) // b is deleted break; if ((c = cpr(cmp, key, n.key)) < 0) break outer; if (c > 0) { b = n; n = f; continue; } if (value != null && !value.equals(v)) //如果value不相等,退出 break outer; if (!n.casValue(v, null)) //无限循环,直至设置节点的值为null成功, break; //之前已经把当前节点值设为null,之后的删除操作分两步:1,在n和n.next间插入一个删除标记节点 //marker; 2,设置b.next为f;这是由两个原子操作共同完成,如果都正常完成,则直接返回;如果有其中一步失败, //则调用findNode(key)来继续完成删除null节点的操作; if (!n.appendMarker(f) || !b.casNext(n, f)) findNode(key); // retry via findNode else { . findPredecessor(key, cmp); // clean index if (head.right == null) tryReduceLevel(); //最上面三层都无索引节点,则把最上面一层的索引删除。 } @SuppressWarnings("unchecked") V vv = (V)v; return vv; } } return null; } /** * 添加一个删除标记节点,设置当前节点的next节点为new Node(f),该新增节点的value值为当前节点f.value=f; * Tries to append a deletion marker to this node. * @param f the assumed current successor of this node * @return true if successful */ boolean appendMarker(Node<K,V> f) { return casNext(f, new Node<K,V>(f)); } Node(Node<K,V> next) { this.key = null; this.value = this; this.next = next; } /** * compareAndSet next field */ boolean casNext(Node<K,V> cmp, Node<K,V> val) { return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } /** * 继续完成删除节点过程: * Helps out a deletion by appending marker or unlinking from * predecessor. This is called during traversals when value * field seen to be null. * @param b predecessor * @param f successor */ void helpDelete(Node<K,V> b, Node<K,V> f) { /* * Rechecking links and then doing only one of the * help-out stages per call tends to minimize CAS * interference among helping threads. */ if (f == next && this == b.next) { if (f == null || f.value != f) // not already marked 判断是否为marker节点(f.value=f) casNext(f, new Node<K,V>(f)); else b.casNext(this, f.next); } }
删除步骤:
1, 删除前,需要删除节点n:

2,删除时,先设置n.value= null; 无限循环,直至成功为止;
3,添加删除标记节点marker:

marker 节点的key=null, value=f, next=f;
4,删除该节点n以及标记节点marker:

其中步骤2,3,4分别由3个CAS原子操作完成。步骤2保证成功,步骤3或者4只要有一个失败,此时的n.value = null, 因此都可以由节点遍历的操作来继续推进删除过程。
为什么删除操作要分为3步,而不是由一个步骤(步骤4)来完成?因为CAS操作只能保证一个变量操作的原子性。
相关推荐
对JDK 7源码的学习,是深入理解Java语言底层实现、提升编程技能的关键步骤。 1. **多线程与并发改进** JDK 7在多线程和并发处理方面做了优化,如Fork/Join框架,它提供了一种将大任务拆分成小任务并行执行的策略,...
集合源码 基本 List, Set, Queue, Map ArrayList, LinkedList, Vector, CopyOnWriteArrayList HashMap JDK7, JDK8 ConcurrentHashMap JDK7, JDK8 HashTable, ThreadLocalMap, WeakHashMap Leetcode 刷题记录 栈实现...
高并发编程第三阶段11讲 AtomicXXXFieldUpdater源码分析及使用场景分析.mp4 高并发编程第三阶段12讲 sun.misc.Unsafe介绍以及几种Counter方案性能对比.mp4 高并发编程第三阶段13讲 一个JNI程序的编写,通过...
高并发编程第三阶段11讲 AtomicXXXFieldUpdater源码分析及使用场景分析.mp4 高并发编程第三阶段12讲 sun.misc.Unsafe介绍以及几种Counter方案性能对比.mp4 高并发编程第三阶段13讲 一个JNI程序的编写,通过...
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
ACM动态规划模板-区间修改线段树问题模板
# 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
本项目为Python语言开发的PersonRelationKnowledgeGraph设计源码,总计包含49个文件,涵盖19个.pyc字节码文件、12个.py源代码文件、8个.txt文本文件、3个.xml配置文件、3个.png图片文件、2个.md标记文件、1个.iml项目配置文件、1个.cfg配置文件。该源码库旨在构建一个用于表示和查询人物关系的知识图谱系统。
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
rtsp实时预览接口URL:/evo-apigw/admin/API/MTS/Video/StartVideo HLS、FLV、RTMP实时预览接口方式 :接口URL/evo-apigw/admin/API/video/stream/realtime 参数名 必选 类型 说明 data true string Json串 +channelId true string 视频通道编码 +streamType true string 码流类型:1=主码流, 2=辅码流,3=辅码流2 +type true string 协议类型:hls,hlss,flv,flvs,ws_flv,wss_flv,rtmp hls:http协议,m3u8格式,端口7086; hlss:https协议,m3u8格式,端口是7096; flv:http协议,flv格式,端口7886; flvs:https协议,flv格式,端口是7896; ws_flv:ws协议,flv格式,端口是7886; wss_flv:wss协议,flv格式,端口是7896; rtmp:rtmp协议,端口是1975;
Simulink永磁风机飞轮储能系统二次调频技术研究:频率特性分析与参数优化,Simulink永磁风机飞轮储能二次调频技术:系统频率特性详解及参数优化研究参考详实文献及两区域系统应用,simulink永磁风机飞轮储能二次调频,系统频率特性如下,可改变调频参数改善频率。 参考文献详细,两区域系统二次调频。 ,核心关键词: 1. Simulink 2. 永磁风机 3. 飞轮储能 4. 二次调频 5. 系统频率特性 6. 调频参数 7. 改善频率 8. 参考文献 9. 两区域系统 以上关键词用分号(;)分隔,结果为:Simulink;永磁风机;飞轮储能;二次调频;系统频率特性;调频参数;改善频率;参考文献;两区域系统。,基于Simulink的永磁风机与飞轮储能系统二次调频研究:频率特性及调频参数优化
MATLAB驱动的ASR防滑转模型:PID与对照控制算法对比,冰雪路面条件下滑移率与车速轮速对照展示,MATLAB驱动的ASR防滑转模型:PID与对照控制算法对比,冰雪路面条件下滑移率与车速轮速对照图展示,MATLAB驱动防滑转模型ASR模型 ASR模型驱动防滑转模型 ?牵引力控制系统模型 选择PID控制算法以及对照控制算法,共两种控制算法,可进行选择。 选择冰路面以及雪路面,共两种路面条件,可进行选择。 控制目标为滑移率0.2,出图显示车速以及轮速对照,出图显示车辆轮胎滑移率。 模型简单,仅供参考。 ,MATLAB; ASR模型; 防滑转模型; 牵引力控制系统模型; PID控制算法; 对照控制算法; 冰路面; 雪路面; 控制目标; 滑移率; 车速; 轮速。,MATLAB驱动的ASR模型:PID与对照算法在冰雪路面的滑移率控制研究
芯片失效分析方法介绍 -深入解析芯片故障原因及预防措施.pptx
4131_127989170.html
内容概要:本文提供了一个全面的PostgreSQL自动化部署解决方案,涵盖智能环境适应、多平台支持、内存与性能优化以及安全性加强等重要方面。首先介绍了脚本的功能及其调用方法,随后详细阐述了操作系统和依赖软件包的准备过程、配置项的自动生成机制,还包括对实例的安全性和监控功能的强化措施。部署指南给出了具体的命令操作指导,便于新手理解和执行。最后强调了该工具对于不同硬件条件和服务需求的有效应对能力,特别是针对云计算环境下应用的支持特点。 适合人群:对PostgreSQL集群运维有一定基础并渴望提高效率和安全性的数据库管理员及工程师。 使用场景及目标:本脚本能够帮助企业在大规模部署时减少人工介入时间,确保系统的稳定性与高性能,适用于各类需要稳定可靠的数据库解决方案的企业或机构,特别是在大数据量和高并发事务处理场合。 其他说明:文中还提及了一些高级功能如自动备份、流复制等设置步骤,使得该方案不仅可以快速上线而且能满足后续维护和发展阶段的要求。同时提到的技术性能数据也为用户评估其能否满足业务需求提供了直观参考。
房地产开发合同[示范文本].doc
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!
在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!