归纳:java
这个哈希表的主要设计目标是维护并发可读性(一般方法get(),但也迭代器和相关方法),同时最小化更新争用。node
次要的目标是保持空间消耗与java.util相同或更好。数组
HashMap,并支持high多个线程对空表的初始插入率。并发
这个映射一般充当一个binned (bucked)哈希表。每个键值映射保存在节点中。大多数节点都是实例包含散列、键、值和next的基本节点类字段。线程
然而,存在各类子类:treenode排列成平衡的树,而不是列表。树冠支撑着树根一组树状阳极。转发节点位于头部调整大小期间的桶。reservationnode用做设计
占位符,同时在computeIfAbsent和相关的方法。类型为TreeBin、forwarding节点和ReservationNode不保存正常的用户密钥、值或散列,以及在搜索过程当中容易区分等。由于它们有负哈希字段和空键和值字段。(这些特殊的节点不是不常见就是短暂的,所以,携带一些未使用的字段的影响是微不足道。)指针
时,将该表惰性地初始化为2的幂级大小第一次插入。表中的每一个bin一般包含一个节点列表(一般,列表只有0个或一个节点)。表访问须要volatile/原子读、写和案件。由于没有其余方法来安排这个添加进一步的间接,咱们使用了intrinsics (sun.misc.Unsafe)操做。对象
咱们使用节点哈希字段的顶部(符号)位进行控制目的——因为寻址,不管如何它都是可用的约束。具备负哈希字段的节点是特殊的在map方法中处理或忽略。索引
方法中第一个节点的插入(经过put或其变体)空桶是经过将其装箱到桶中来执行的。这是到目前为止,put操做在most下最多见的状况键/散列分布。其余更新操做(插入、删除和替换)须要锁。咱们不想浪费将一个不一样的锁对象与之关联所需的空间每一个bin,所以使用bin列表自己的第一个节点做为一个锁。对这些锁的锁定支持依赖于内置监控“同步”。ip
将列表的第一个节点用做锁自己并不会这样作足够了:当一个节点被锁定时,任何更新都必须首先进行确认它仍然是锁定后的第一个节点,而且若是没有,请重试。由于新节点老是附加到列表中,一旦一个节点在一个bin中第一个被删除,它就会保持在第一个直到被删除或bin(调整大小后)失效。
每一个bin锁的主要缺点是其余更新在受相同保护的bin列表中的其余节点上执行操做lock可能会中止,例如当user =()或映射时功能须要很长时间。然而,据统计,
随机哈希码,这不是一个常见的问题。理想状况下,箱中节点的频率服从泊松分布(http://en.wikipedia.org/wiki/Poisson_distribution)给定调整阈值,参数平均约为0.5为0.75,但因为调整大小,方差较大粒度。忽略方差,指望发生的列表大小k是(exp(-0.5) * pow(0.5, k) / factorial(k))。的
第一个值是:
0: 0.60653066
1: 0.30326533
2: 0.07581633
3: 0.01263606
4: 0.00157952
5: 0.00015795
6: 0.00001316
7: 0.00000094
8: 0.00000006
多于:少于千分之一
两个访问不一样的线程的锁争用几率元素大约是随机哈希下的1 /(8 * #元素)。
实际中遇到的哈希代码分布有时明显偏离统一的随机性。这个包括n>时的状况(1<<30),所以某些键必须碰撞。一样地,对于多个键都是设计为具备相同的哈希代码或仅不一样的哈希代码隐藏的高位。因此咱们使用了一个二级策略当bin中的节点数超过阈值。这些树键使用平衡树来保存节点(a特殊形式的红黑树),边界搜索时间O(log n)。treebin中的每一个搜索步骤至少是和普通的列表同样慢,但考虑到n不能超过(1<<64)(在地址用完以前)此边界搜索步进、锁定保持时间等,达到合理的常数(大体每一个操做检查100个节点(最坏状况),只要键具备可比性(很是常见——字符串、长字符串等)。treebin节点(treenodes)也保持相同的“next”遍历指针做为常规节点,所以能够遍历以一样的方式使用迭代器。
当占用率超过某个百分比时,将调整表的大小。阈值(名义上为0.75,但见下文)。任何线程注意到垃圾箱过满可能有助于在启动线程分配并设置替换数组。可是,这些其余线程可能会继续运行,而不是中止运行。插入等。使用treebins保护咱们不受调整大小时过分填充的最坏状况影响进步。经过一个接一个地传送垃圾箱来调整收益大小,从桌子到下一张桌子。然而,线程声称很小以前要传输的索引块(经过字段transferIndex)这样作能够减小争用。田间的世代印记sizectl确保大小调整不会重叠。由于咱们是使用两次扩展的力量,每一个箱子的元件必需要么保持同一索引,要么以2的幂移动偏移量。咱们经过捕获旧节点能够重用的状况,由于它们的下一个字段不会改变的。平均来讲,只有大约六分之一的人须要当表翻倍时进行克隆。它们替换的节点将是一旦再也不被引用,即可收集的垃圾任何可能同时处于遍历表。在传输时,旧的表bin包含只有一个特殊的转发节点(哈希字段为“moved”)。包含下一个表做为其键。遇到一个转发节点,访问和更新操做从新启动,使用新桌子。