有关putVal(K key, V value, boolean onlyIfAbsent)
method
每个垃圾箱/桶包含一个hash
领域,它以一种非常聪明的方式结合了两个目的:
- 对于常规 bin(大多数 bin 只包含单个项目),它存储映射到此处的键的哈希码。但最高位被清除(它始终设置为 0)。
- 对于特殊的 bin(目前有 3 种类型),它包含一个特殊的负值。聪明的部分是,您只需要顶部位即可区分正值和负值,从而区分常规垃圾箱和特殊垃圾箱。区分不同类型的特殊 bin 可以自由使用剩余的 31 位。
本节
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {//...}
是发现映射不为空并且您尝试映射的键的存储箱不为空后进行的第一次检查。
如果您找到的垃圾箱是特殊类型的垃圾箱之一 - 转发垃圾箱,则满意。需要转发箱,因为调整大小是同时迭代完成的,并且已经传输(到新表)的条目仍然需要可访问(通过旧表中的转发箱)。
有关casTabAt((Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v)
method
The casTabAt()
方法用于使用对象引用的比较和交换操作以原子方式设置映射条目。您仍然可以在几乎所有需要的地方看到典型的 CAS 循环casTabAt()
使用 - 您构造要放置的对象,然后尝试将其 CAS 到其正确的位置。如果在 CAS 尝试之前进行复杂的构造感觉很奇怪,你可以看看 Jeff Preshing 的您可以执行任何类型的原子读-修改-写操作.
从某种意义上说,ConcurrentHashMap
仍然使用条带锁定,但具有更精细的锁定粒度(竞争区域现在从多仓段到单个仓最小化)并且锁几乎完全被 CAS 操作取代。
有关get(Object key)
method
The get()
方法可以在没有任何锁定的情况下逃脱,因为在大多数情况下,bin 内容是使用设置和检索的volatile
语义(通过前面提到的casTabAt()
方法及相关tabAt()
方法)。如果 bin 包含映射到同一个 bin 的红黑树条目,情况会更加棘手,并且您可以看到访问的条目内的遍历TreeBin
总是在 a 中完成synchronized
block.