【JUC】JDK1.8源码分析之ConcurrentSkipListMap(二)
一、前言
最近在做项目的同时也在修复之前项目的一些Bug,所以忙得没有时间看源代码,今天都完成得差不多了,所以又开始源码分析之路,也着笔记录下ConcurrentSkipListMap的源码的分析过程。
二、ConcurrentSkipListMap数据结构
抓住了数据结构,对于理解整个ConcurrentSkipListMap有很重要的作用,其实,通过源码可知其数据结构如下。
说明:可以看到ConcurrentSkipListMap的数据结构使用的是跳表,每一个HeadIndex、Index结点都会包含一个对Node的引用,同一垂直方向上的Index、HeadIndex结点都包含了最底层的Node结点的引用。并且层级越高,该层级的结点(HeadIndex和Index)数越少。Node结点之间使用单链表结构。
三、ConcurrentSkipListMap源码分析
3.1 类的继承关系
public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable {}
说明:ConcurrentSkipListMap继承了AbstractMap抽象类,实现了ConcurrentNavigableMap接口,该接口定义了获取某一部分集合的操作。实现了Cloneable接口,表示允许克隆。实现了Serializable接口,表示可被序列化。
3.2 类的内部类
ConcurrentSkipListMap包含了很多内部类,内部类的框架图如下:
说明:其中,最为重要的类包括Index、HeadIndex、Node三个类。下面对这三个类进行逐一讲解,其他的类,读者有兴趣可以自行分析。
① Index类
1. 类的属性
static class Index<K,V> {final Node<K,V> node;final Index<K,V> down;volatile Index<K,V> right;// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long rightOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> k = Index.class;rightOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("right"));} catch (Exception e) {throw new Error(e);}}}
说明:可以看到,Index结点包括一个Node结点的引用,都是包含down域和right域,即对应数据结构中的Index结点。并且借助了反射来原子性的修改right域。
2. 类的构造函数
/*** Creates index node with given values.*/Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {this.node = node;this.down = down;this.right = right;}
View Code
说明:构造Index结点,确定Node引用,down域和right域。
3. 核心函数分析
3.1 link函数
final boolean link(Index<K,V> succ, Index<K,V> newSucc) {// 获取Index结点的Node结点Node<K,V> n = node;// 将newSucc结点的right域设置为succnewSucc.right = succ;// 结点的值不为空并且比较并交换当前Index结点的right域(将当前Index(this)结点的right域设置为newSucc)return n.value != null && casRight(succ, newSucc);}
View Code
说明:link方法用于在当前Index结点的后面插入一个Index结点,形成其right结点。并且插入的Index结点的right域为当前结点的right域。
3.2 unlink函数
final boolean unlink(Index<K,V> succ) {// 当前Index结点的Node结点的值不为空并且将当前Index结点的right设置为succ的right结点return node.value != null && casRight(succ, succ.right);}
View Code
说明:unlink方法与link方法作用相反,其删除当前Index结点的right结点,即将当前Index结点的right指向当前Index结点的right.right域。
② HeadIndex类
// 头结点索引类static final class HeadIndex<K,V> extends Index<K,V> {// 层级final int level;// 构造函数HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {// 构造Index类super(node, down, right);this.level = level;}}
说明:根据HeadIndex类可知其继承自Index类,并且在Index类的基础上添加了level域,表示当前的层级。
③ Node类
1. 类的属性
static final class Node<K,V> {// 键final K key;// 值volatile Object value;// 下一个结点volatile Node<K,V> next;// UNSAFE mechanicsprivate static final sun.misc.Unsafe UNSAFE;// value域的偏移地址private static final long valueOffset;// next域的偏移地址private static final long nextOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> k = Node.class;valueOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("value"));nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next"));} catch (Exception e) {throw new Error(e);}}}
View Code
说明:Node类包含了key、value、next域,其是用来实际存放元素的结点,并且是使用单链表结构。同时,也使用了反射来原子性的修改value与和next域。
2. 类的构造函数
Node(K key, Object value, Node<K,V> next) {this.key = key;this.value = value;this.next = next;}/*** Creates a new marker node. A marker is distinguished by* having its value field point to itself. Marker nodes also* have null keys, a fact that is exploited in a few places,* but this doesn't distinguish markers from the base-level* header node (head.node), which also has a null key.*/// 用于建立标记结点,值为本身Node(Node<K,V> next) {this.key = null;this.value = this;this.next = next;}
View Code
说明:Node类包含了两种构造函数,分别表示正常的结点和marker标记结点,marker标记结点在删除结点时被使用。
3. 类的核心函数
3.1 helpDelete函数
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) { // f为当前结点的后继并且b为当前结点的前驱if (f == null || f.value != f) // f为空或者f的value不为本身,即没有被标记 not already marked// 当前结点后添加一个marker结点,并且当前结点的后继为marker,marker结点的后继为fcasNext(f, new Node<K,V>(f)); else // f不为空并且f的值为本身// 设置b的next域为f的next域b.casNext(this, f.next);}}
View Code
说明:删除结点,在结点后面添加一个marker结点或者将结点和其后的marker结点从其前驱中断开。
3.3 类的属性
public class ConcurrentSkipListMap<K,V> extends AbstractMap<K,V>implements ConcurrentNavigableMap<K,V>, Cloneable, Serializable {// 版本序列号 private static final long serialVersionUID = -8627078645895051609L;// 基础层的头结点private static final Object BASE_HEADER = new Object();// 最顶层头结点的索引private transient volatile HeadIndex<K,V> head;// 比较器final Comparator<? super K> comparator;// 键集合private transient KeySet<K> keySet;// entry集合private transient EntrySet<K,V> entrySet;// 值集合private transient Values<V> values;// 降序键集合private transient ConcurrentNavigableMap<K,V> descendingMap;// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;// head域的偏移量private static final long headOffset;// Thread类的threadLocalRandomSecondarySeed的偏移量private static final long SECONDARY;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> k = ConcurrentSkipListMap.class;headOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("head"));Class<?> tk = Thread.class;SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));} catch (Exception e) {throw new Error(e);}} }
View Code
说明:ConcurrentSkipListMap包含了head属性,表示跳表的头结点,并且包含了一个比较器,值得注意的是,对于ConcurrentSkipListMap的使用,键必须能够进行比较,如传递了比较器或者键本身就能够进行比较。同时,也使用了反射来保证原子性的更新head域。
3.4 类的构造函数
1. ConcurrentSkipListMap()型构造函数
// 构造一个新的空映射,该映射按照键的自然顺序进行排序public ConcurrentSkipListMap() {// 比较器为空,那么键必须能够比较(实现了Comparable接口)this.comparator = null;// 初始化相关的域 initialize();}
View Code
说明:构造一个新的空映射,该映射按照键的自然顺序进行排序,即键K必须实现了Comparable接口,否则,会报错。
2. ConcurrentSkipListMap(Comparator<? super K>)型构造函数
// 构造一个新的空映射,该映射按照指定的比较器进行排序public ConcurrentSkipListMap(Comparator<? super K> comparator) {// 初始化比较器this.comparator = comparator;// 初始化相关的域 initialize();}
View Code
说明:构造一个新的空映射,该映射按照指定的比较器进行排序
3. ConcurrentSkipListMap(Map<? extends K, ? extends V>)型构造函数
// 构造一个新映射,该映射所包含的映射关系与给定映射包含的映射关系相同,并按照键的自然顺序进行排序public ConcurrentSkipListMap(Map<? extends K, ? extends V> m) {// 比较器Wie空this.comparator = null;// 初始化相关的域 initialize();// 将m的所有元素添加至跳表 putAll(m);}
View Code
说明:构造一个新映射,该映射所包含的映射关系与给定映射包含的映射关系相同,并按照键的自然顺序进行排序。
4. ConcurrentSkipListMap(SortedMap<K, ? extends V>)型构造函数
// 构造一个新映射,该映射所包含的映射关系与指定的有序映射包含的映射关系相同,使用的顺序也相同public ConcurrentSkipListMap(SortedMap<K, ? extends V> m) {// 获取m的比较器this.comparator = m.comparator();// 初始化相关的域 initialize();// 根据m的元素来构建跳表 buildFromSorted(m);}
View Code
说明:构造一个新映射,该映射所包含的映射关系与指定的有序映射包含的映射关系相同,使用的顺序也相同。
3.5 核心函数分析
1. doPut函数
// 插入一个结点private V doPut(K key, V value, boolean onlyIfAbsent) {Node<K,V> z; // added nodeif (key == null) // 键为空,抛出空异常throw new NullPointerException();// 比较器Comparator<? super K> cmp = comparator;outer: for (;;) { // 无限循环for (Node<K,V> b = findPredecessor(key, cmp), n = b.next;;) { // 找到先驱结点,n为当前结点if (n != null) { // next域不为空Object v; int c;// f为当前结点的后继节点Node<K,V> f = n.next;if (n != b.next) // 不一致,重试 // inconsistent readbreak;if ((v = n.value) == null) { // n结点已经被删除 // n is deleted// 进行删除 n.helpDelete(b, f);break;}if (b.value == null || v == n) // b结点已经被删除 b is deletedbreak;if ((c = cpr(cmp, key, n.key)) > 0) { // key大于结点的key// b往后移动b = n;// n往后移动n = f;continue;}if (c == 0) { // 键相等if (onlyIfAbsent || n.casValue(v, value)) { // 比较并交换值@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);if (!b.casNext(n, 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 bitsint level = 1, max;while (((rnd >>>= 1) & 1) != 0) // 判断从右到左有多少个连续的1++level;Index<K,V> idx = null;// 保存头结点HeadIndex<K,V> h = head;if (level <= (max = h.level)) { // 小于跳表的层级for (int i = 1; i <= level; ++i) // 为结点生成对应的Index结点// 从下至上依次赋值,并且赋值了Index结点的down域idx = new Index<K,V>(z, idx, null);}else { // try to grow by one levellevel = max + 1; // hold in array and later pick the one to use// 生成Index结点的数组,其中,idxs[0]不作使用@SuppressWarnings("unchecked")Index<K,V>[] idxs =(Index<K,V>[])new Index<?,?>[level+1]; for (int i = 1; i <= level; ++i) // 从下到上生成Index结点,并赋值down域idxs[i] = idx = new Index<K,V>(z, idx, null);for (;;) { // 无限循环// 保存头结点h = head;// 保存跳表之前的层级int oldLevel = h.level;if (level <= oldLevel) // lost race to add levelbreak;// 保存头结点HeadIndex<K,V> newh = h;// 保存头结点对应的Node结点Node<K,V> oldbase = h.node;for (int j = oldLevel+1; j <= level; ++j) // 为每一层生成一个头结点newh = new HeadIndex<K,V>(oldbase, newh, idxs[j], j);if (casHead(h, newh)) { // 比较并替换头结点// h赋值为最高层的头结点h = newh;// idx赋值为之前层级的头结点,并将level赋值为之前的层级idx = idxs[level = oldLevel];break;}}}// find insertion points and splice in// 插入Index结点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) // 头结点或者idx结点为空// 跳出外层循环break splice;if (r != null) { // right结点不为空// 保存r对应的Node结点Node<K,V> n = r.node;// compare before deletion check avoids needing recheck// 比较key与结点的key值int c = cpr(cmp, key, n.key);if (n.value == null) { // 结点的值为空,表示需要删除if (!q.unlink(r)) // 删除q的Index结点break;// r为q的right结点r = q.right;continue;}if (c > 0) { // key大于结点的key// 向右寻找q = r;r = r.right;continue;}}if (j == insertionLevel) {if (!q.link(r, t)) // r结点插入q与t之间break; // restartif (t.node.value == null) { // t结点的值为空,需要删除// 利用findNode函数的副作用 findNode(key);break splice;}if (--insertionLevel == 0) // 到达最底层,跳出循环break splice;}if (--j >= insertionLevel && j < level) t = t.down;q = q.down;r = q.right;}}}return null;}
View Code
说明:doPut提供对put函数的支持,doPut的大体流程如下:
① 根据给定的key从跳表的左上方往右或者往下查找到Node链表的前驱Node结点,这个查找过程会删除一些已经标记为删除的结点。
② 找到前驱结点后,开始往后插入查找插入的位置(因为找到前驱结点后,可能有另外一个线程在此前驱结点后插入了一个结点,所以步骤①得到的前驱现在可能不是要插入的结点的前驱,所以需要往后查找)。
③ 随机生成一个种子,判断是否需要增加层级,并且在各层级中插入对应的Index结点。
其中,会调用到findPredecessor函数,findPredecessor函数源码如下
private Node<K,V> findPredecessor(Object key, Comparator<? super K> cmp) {if (key == null) // 键为空,抛出空异常throw new NullPointerException(); // don't postpone errorsfor (;;) { // 无限循环for (Index<K,V> q = head, r = q.right, d;;) { // if (r != null) { // 右Index结点不为空// n为当前Node结点Node<K,V> n = r.node;// 为当前keyK k = n.key;if (n.value == null) { // 当前Node结点的value为空,表示需要删除if (!q.unlink(r)) // unlink r Index结点break; // restart// r为rightIndex结点r = q.right; // reread rcontinue;}if (cpr(cmp, key, k) > 0) { // 比较key与当前Node结点的k,若大于0// 向右移动q = r;r = r.right;continue;}}if ((d = q.down) == null) // q的down域为空,直接返回q对应的Node结点return q.node;// 向下移动q = d;// d的right结点r = d.right;}}}
View Code
说明:findPredecessor函数的主要流程如下。
从头结点(head)开始,先比较key与当前结点的key的大小,若key大于当前Index结点的key并且当前Index结点的right不为空,则向右移动,继续查找;若当前Index结点的right为空,则向下移动,继续查找;若key小于等于当前Index结点的key,则向下移动,继续查找。直至找到前驱结点。
2. doRemove函数
// 移除一个结点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;;) { // 根据key找到前驱结点,n为当前Index结点Object v; int c;if (n == null) // n不为空break outer;// f为当前结点的next结点Node<K,V> f = n.next; if (n != b.next) // 不一致,重试 // inconsistent readbreak;if ((v = n.value) == null) { // 当前结点的value为空,需要删除 // n is deleted// 删除n结点 n.helpDelete(b, f);break;}if (b.value == null || v == n) // b is deletedbreak;if ((c = cpr(cmp, key, n.key)) < 0) // key小于当前结点的key// 跳出外层循环break outer;if (c > 0) { // key大于当前结点的key// 向后移动b = n;n = f;continue;}if (value != null && !value.equals(v))break outer;if (!n.casValue(v, null)) // 当前结点的value设置为nullbreak;if (!n.appendMarker(f) || !b.casNext(n, f)) // 在n结点后添加一个marker结点,并且将b的next域更新为ffindNode(key); // retry via findNodeelse { // 添加节点并且更新均成功// 利用findNode函数的副作用,删除n结点对应的Index结点findPredecessor(key, cmp); // clean indexif (head.right == null) // 头结点的right为null// 需要减少层级 tryReduceLevel();}@SuppressWarnings("unchecked") V vv = (V)v;return vv;}}return null;}
View Code
说明:doRemove函数的处理流程如下。
① 根据key值找到前驱结点,查找的过程会删除一个标记为删除的结点。
② 从前驱结点往后查找该结点。
③ 在该结点后面添加一个marker结点,若添加成功,则将该结点的前驱的后继设置为该结点之前的后继。
④ 头结点的next域是否为空,若为空,则减少层级。
下面的示意图给出了remove操作一种可能的情况(仅仅涉及Node结点的链表层的操作)
说明:可以看到remove操作是分为两步进行的,首先是在要删除结点的后面添加一个marker结点,然后修改删除结点的前驱结点的next域。注意,这里仅仅只给出了Node结点的链表层的操作,并没有涉及到Index结点,关于Index结点的情况,之后会给出一个示例。其中会调用到tryReduceLevel函数,tryReduceLevel源码如下
// 减少跳表层级private void tryReduceLevel() {// 保存头结点HeadIndex<K,V> h = head;HeadIndex<K,V> d;HeadIndex<K,V> e;if (h.level > 3 &&(d = (HeadIndex<K,V>)h.down) != null &&(e = (HeadIndex<K,V>)d.down) != null &&e.right == null &&d.right == null &&h.right == null &&casHead(h, d) && // try to seth.right != null) // recheckcasHead(d, h); // try to backout}
View Code
说明:如果最高的前三个HeadIndex不为空,并且其right域都为null,那么就将level减少1层,并将head设置为之前head的下一层,设置完成后,还有检测之前的head的right域是否为null,如果为null,则减少层级成功,否则再次将head设置为h。
3. doGet函数
private V doGet(Object key) {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找到前驱结点,n为当前结点Object v; int c;if (n == null) // 当前Index结点为null,跳出外层循环break outer;// f为当前结点的next结点Node<K,V> f = n.next;if (n != b.next) // 不一致,重试 // inconsistent readbreak;if ((v = n.value) == null) { // n is deleted n.helpDelete(b, f);break;}if (b.value == null || v == n) // b is deletedbreak;if ((c = cpr(cmp, key, n.key)) == 0) { // 找到key值相等的结点@SuppressWarnings("unchecked") V vv = (V)v;// 返回valuereturn vv;}if (c < 0) // 小于当前结点// 则表示没有找到,跳出外层循环break outer;// 继续向后移动b = n;n = f;}}return null;}
View Code
说明:doGet函数流程比较简单,首先根据key找到前驱结点,然后从前驱结点开始往后查找,找到与key相等的结点,则返回该结点,否则,返回null。在这个过程中会删除一些已经标记为删除状态的结点。
4. size函数
public int size() {long count = 0;for (Node<K,V> n = findFirst(); n != null; n = n.next) { // 找到第一个结点if (n.getValidValue() != null) // n结点没有被标记删除// 计数器加1++count;}return (count >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) count;}
View Code
说明:size函数的流程如下,首先利用findFirst函数找到第一个value不为null的结点。然后开始往后遍历,调用Node结点的getValidValue函数判断结点的value是否有效,有效则计数器加1。其中findFirst源码如下
final Node<K,V> findFirst() {for (Node<K,V> b, n;;) {if ((n = (b = head.node).next) == null) // 头结点的下一个结点为当前结点,为null// 返回nullreturn null;if (n.value != null) // 当前结点不为null// 则返回该结点return n;// 表示当前结点的value为null,则进行删除 n.helpDelete(b, n.next);}}
View Code
说明:findFirst函数的功能是找到第一个value不为null的结点。getValidValue源码如下
V getValidValue() {Object v = value;if (v == this || v == BASE_HEADER) // value为自身或者为BASE_HEADERreturn null;@SuppressWarnings("unchecked") V vv = (V)v;return vv;}
View Code
说明:若结点的value为自身或者是BASE_HEADER,则返回null,否则返回结点的value。
四、示例
下面通过一个简单的示例,来深入了解ConcurrentSkipListMap的内部结构。
package com.hust.grid.leesf.collections;import java.util.concurrent.ConcurrentSkipListMap;public class ConcurrentSkipListMapDemo {public static void main(String[] args) {ConcurrentSkipListMap<String, Integer> cslm = new ConcurrentSkipListMap<String, Integer>();cslm.put("leesf", 24);cslm.put("dyd", 24);for (String key :cslm.keySet()) {System.out.print("[" + key + "," + cslm.get(key) + "] ");}System.out.println();cslm.remove("leesf");for (String key :cslm.keySet()) {System.out.print("[" + key + "," + cslm.get(key) + "] ");}} }
View Code
运行结果:
[dyd,24] [leesf,24]
[dyd,24]
说明:上面的一个示例非常简单,下面借这个示例,来分析ConcurrentSkipListMap的内部结构。
① 当新生一个ConcurrentSkipListMap时,有如下结构。
② 当put("leesf", 24)后,可能有如下结构
说明:在插入一个Node结点的同时,也插入一个Index结点,并且head结点的right域指向该Index结点,该Index的Node域指向插入的Node结点。
③ 当put("dyd", 24)后,可能有如下结构
说明:插入的("dyd", 24),新生成的结点在leesf结点之前,并且也生成了一个Index结点指向它,此时跳表的层级还是为1。
④ 同样,当put("dyd", 24)后,也可能有如下结构
说明:在插入("dyd", 24)后,层级加1,此时会生成两个Index结点,并且两个Index结点均指向新生成的Node结点。
⑤ 在remove("dyd")之后,存在如下结构
说明:在key为dyd的结点后面添加了一个marker结点(key为null,value为自身),并且Node结点对应的Index也将从Index链表中断开,最后会被GC。
五、总结
看源代码需要耐心,多思考,才能领略源码的魅力所在,也谢谢各位园友的观看~
参考链接:http://brokendreams.iteye.com/blog/2253955
转载于:https://www.cnblogs.com/leesf456/p/5512817.html
【JUC】JDK1.8源码分析之ConcurrentSkipListMap(二)相关推荐
- 【JUC】JDK1.8源码分析之ArrayBlockingQueue(三)
一.前言 在完成Map下的并发集合后,现在来分析ArrayBlockingQueue,ArrayBlockingQueue可以用作一个阻塞型队列,支持多任务并发操作,有了之前看源码的积累,再看Arra ...
- 【集合框架】JDK1.8源码分析之HashMap(一)
转载自 [集合框架]JDK1.8源码分析之HashMap(一) 一.前言 在分析jdk1.8后的HashMap源码时,发现网上好多分析都是基于之前的jdk,而Java8的HashMap对之前做了较大 ...
- 【集合框架】JDK1.8源码分析HashSet LinkedHashSet(八)
一.前言 分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet ...
- JDK1.8源码分析:可重入锁ReentrantLock和Condition的实现原理
synchronized的用法和实现原理 synchronized实现线程同步的用法和实现原理 不足 synchronized在线程同步的使用方面,优点是使用简单,可以自动加锁和解锁,但是也存在一些不 ...
- idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)
课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...
- synchronousqueue场景_【JUC】JDK1.8源码分析之SynchronousQueue(九)
一.前言 本篇是在分析Executors源码时,发现JUC集合框架中的一个重要类没有分析,SynchronousQueue,该类在线程池中的作用是非常明显的,所以很有必要单独拿出来分析一番,这对于之后 ...
- 【JUC】JDK1.8源码分析之ConcurrentHashMap
一.前言 最近几天忙着做点别的东西,今天终于有时间分析源码了,看源码感觉很爽,并且发现ConcurrentHashMap在JDK1.8版本与之前的版本在并发控制上存在很大的差别,很有必要进行认真的分析 ...
- 【JUC】JDK1.8源码分析之ConcurrentLinkedQueue(五)
一.前言 接着前面的分析,接下来分析ConcurrentLinkedQueue,ConcurerntLinkedQueue一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素 ...
- 【JUC】JDK1.8源码分析之AbstractQueuedSynchronizer
一.前言 在锁框架中,AbstractQueuedSynchronizer抽象类可以毫不夸张的说,占据着核心地位,它提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.所以很有必 ...
- Java中ConcurrentHashMap底层实现原理(JDK1.8)源码分析2
https://blog.csdn.net/programmer_at/article/details/79715177 https://blog.csdn.net/qq_41737716/categ ...
最新文章
- 年度盘点!必看AI顶会论文、Github高星项目大合集(附链接)
- amuse ui(web插件,js插件,css样式)?
- 用北斗和阿里云毫米级监控山体滑坡 他还只是铁路工人
- python c 语言接口,## 人生苦短我用python[0x08] 使用ctypes调用c语言接口 ##
- lEO数值资产系统某平台c2c币数值合约交易平台自动撮合松机器人功能
- 问题及解决 —— 浏览器问题
- 巨蟒django之CRM2 展示客户列表分页
- Android逆向分析实例(三)-解密微信EnMicroMsg.db数据库
- 颜色表大全 颜色中英文对照表
- AutoCAD 2019 mac中文
- 大学四年,我是怎么靠做外包私活赚了10w+,实现经济独立
- java实现思维导图_Java并发(思维导图)
- 如何在 Chrome、Firefox 和 Edge 中进行硬刷新?
- logstash的dissect匹配字符串内置双引号时需要注意的问题
- 企业精益生产之成本管理控制的四大要点
- 企查查app sign算法破解(完结)
- 当路町-网络下载应用系列之二-破解网页内容无法复制
- zblog php模板偷,zblogPHP仿站+定制模板 - 模板ID code
- 艺术聚焦:#DRIVE
- SAP BDC 数据导入
热门文章
- NHibernate的缓存管理机制
- ToolStripContainer
- Python之进程、线程、锁
- BZOJ.4337.[BJOI2015]树的同构(树哈希)
- 记一份电网信息化建设企业信息分析平台规划
- TextureMerger1.6.6 一:Egret MovieClip的制作和使用
- ASP.NET之MVC 微信公众号授权给第三方平台的技术实现流程一(获取第三方平台access_token)...
- navigationcontroller和navigationbar和navigationitem之间的区别以及不用nib实现点击屏幕关闭虚拟键盘20130911...
- 【Gbase】建表时候hash分布列的制定方式(DISTRIBUTED BY column_name)
- 线性规划中的单纯形法与内点法(原理、步骤以及matlab实现)(二)