经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
探究ConcurrentHashMap中键值对在Segment[]的下标如何确定
来源:cnblogs  作者:比脚更长的路  时间:2018/10/9 10:15:55  对本文有异议

内容

   本文对JDK1.7下使用segmentShift和segmentMask求解ConcurrentHashMap键值对在Segment[]中的下标值进行了探究和论证。

适合人群

 ?  Java进阶

说明

   转载请注明出处,尊重笔者的劳动成果。

 推荐阅读

   探究HashMap线性不安全(二)——链表成环的详细过程

 正文

   下面先查看ConcurrentHashMap源码中的put操作,找到segment[]的下标j的计算公式。

  1. 1 @SuppressWarnings("unchecked")
  2. 2 public V put(K key, V value) {
  3. 3 Segment<K,V> s;
  4. 4 if (value == null)
  5. 5 throw new NullPointerException();
  6. 6 int hash = hash(key);
  7. 7 //key对应的segment[]的下标j的计算公式
  8. 8 int j = (hash >>> segmentShift) & segmentMask;
  9. 9 if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
  10. 10 (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
  11. 11 s = ensureSegment(j);
  12. 12 return s.put(key, hash, value, false);
  13. 13 }

  由上面的ConcurrentHashMap源码可知,一个键值对在Segment数组中下标j的计算公式为:

  1. j = (hash >>> segmentShift) & segmentMask

  公式虽然不长,但是它包含了2个“晦涩难懂”的参数:segmentShift和segmentMask ,让人费解。下面笔者用一种通俗简单的方式来解释该公式的含义。

  首先,阅读ConcurrentHashMap的构造方法,重点查看注释区域,其中包含了segmentShift和segmentMask的定义:

  1. segmentShift = 32 - sshift;segmentMask = ssize - 1;

  以及segment数组长度ssize与sshift的关系:

  1. 2^sshif=ssize
  1.   ConcurrentHashMap的构造方法
  1. 1 public ConcurrentHashMap(int initialCapacity,
  2. 2 float loadFactor, int concurrencyLevel) {
  3. 3 if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
  4. 4 throw new IllegalArgumentException();
  5. 5 if (concurrencyLevel > MAX_SEGMENTS)
  6. 6 concurrencyLevel = MAX_SEGMENTS;
  7. 7 int sshift = 0;
  8. 8 int ssize = 1;
  9. 9 //2^sshif=ssize,例:sshift=4,ssize=16;
  10. 10 //根据concurrentLevel计算得出ssize为segments数组长度
  11. 11 while (ssize < concurrencyLevel) {
  12. 12 ++sshift;
  13. 13 ssize <<= 1;
  14. 14 }
  15. 15 //segmentShift和segmentMask的定义
  16. 16 this.segmentShift = 32 - sshift;
  17. 17 this.segmentMask = ssize - 1;
  18. 18 if (initialCapacity > MAXIMUM_CAPACITY)
  19. 19 initialCapacity = MAXIMUM_CAPACITY;
  20. 20 //计算cap的大小,即Segment中HashEntry的数组长度,cap也一定为2的n次方.
  21. 21 int c = initialCapacity / ssize;
  22. 22 if (c * ssize < initialCapacity)
  23. 23 ++c;
  24. 24 int cap = MIN_SEGMENT_TABLE_CAPACITY;
  25. 25 while (cap < c)
  26. 26 cap <<= 1;
  27. 27 //创建segments数组并初始化第一个Segment,其余的Segment延迟初始化
  28. 28 Segment<K,V> s0 =
  29. 29 new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
  30. 30 (HashEntry<K,V>[])new HashEntry[cap]);
  31. 31 Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
  32. 32 UNSAFE.putOrderedObject(ss, SBASE, s0);
  33. 33 this.segments = ss;
  34. 34 }

   由此可知,求key散列到长度为ssize的Segment数组的下标j,必定有下标j的值域为[0,ssize-1]。由于ssize=2^sshif,那么小标j可以用1个sshift位的二进制数字表示。假如:ssize为16,那么sshift=4,j的值域为[0,15],而[0000b,1111b]就是j的值域;则求key在Segment[]的下标j,就是求key对应的一个散列的4位二进制数值。而ConcurrentHashMap的源码求下标j的方式非常简单,就是取key的hash值的高4位。因此,求key散列到长度为ssize的Segment数组的下标j,就是求key的hash值的高sshift位。

  故有,j=(key.hash>>>(32-sshift))&(ssize-1)。而由源码可知,segmentShift = 32 - sshift,segmentMask = ssize - 1。即:

  1. j=(key.hash>>>(32-sshift))&(ssize-1)=(key.hash>>>segmentShift )&segmentMask。(其中>>>表示无符号右移,空位补0

  以ssize为16为例,演示计算过程:

1538405541238

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号