java-collection-ConcurrentHashMap

结构

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

特点
设计目的是维护并发可读性 如get() 和iterators和相关方法 同时最小化update竞争。
次要目标是保持空间消耗,与HashMap相同 或者更好的空间消耗,并支持多线程在空table上的初始高插入率。

此类及其视图和迭代器实现所有的可选的 {@link Map} and {@link Iterator}接口的方法

不同于 {@link Hashtable},这个类不允许null被用作key或者value

支持一组顺序和并行体,与大多数 {@link Stream} 方法不同,
ConcurrentHashMap 被设计成在map上是线程安全的 通常是明智的
可以同时被其他线程更新。
例如当计算共享注册表中的值的快照摘要有三种操作,每种有四种形式,接受具有键、值、项和(key,value)参数的函数和/或返回值。
因为ConcurrentHashMap 的元素不不以任何特定方式排序,并可在在不同的并行执行中的不同顺序,正确性提供的函数不应依赖于任何排序,或在任何计算正在进行中其他可能暂时改变的对象或值

对{@link java.util.Map.Entry}条目的批量操作对象不支持方法{@code setValue}.

forEach

对每个元素执行给定的动作。
*变量形式对每个元素应用给定的转换。
*在执行该动作之前

search
返回第一个可用的非空结果,在每个元素上应用给定函数;*当找到结果时进行搜索。

reduce
累加每个元素。供给减少函数不能依赖于排序(更正式),它应该是结合和交换

Plain reductions
(这种方法没有一种形式(key,value)函数参数,因为没有对应返回类型)
Mapped reductions 得到的结果应用于每个元素的函数
对标量双倍、long和int的缩减,使用给定基值。

这些大容量操作接受 {@code parallelismThreshold}.
如果当前map大销售被估计小于给定的阈值,方法操作是连续的。
使用一个值 {@code Long.MAX_VALUE}抑制所有并行性。
使用1的结果 在一个通过划分成最大并行性。够的子任务以充分利用{@link
* ForkJoinPool#commonPool()} 计算。通常,您将首先选择这些极值其中之一。
然后测量在两者之间使用的性能权衡开销与吞吐量的值。

@code get(key)} 返回的任何非空结果和相关的访问方法承担一个
在与相关插入、更新 happens-before 。任何批量操作的结果反映了每个元素关系的组成,但不是对于整个地图来说,必须是原子,除非它不知何故是静止的。相反相反,因为钥匙图中的值从不为空,NULL作为可靠的值。
原子指示器当前缺少任何结果维护此属性,NULL作为隐式基础所有非标量减少操作
对于 the double, long, and int 版本,基础应该是一个,当与任何其他值,返回其他值、更正式地,它)
*应该是还原的标识元素。最常见减法具有这些性质,例如,计算一个和以基0或最小值为基础的Max值。

作为参数提供的搜索和转换函数类似地返回null以指示缺少任何结果。
(在这种情况下不使用)。在映射的情况下
*减少,这也使得转换充当过滤器,返回null(或,在原始情况下)
*专业化,身份基础)如果元素不应该
*合并。您可以创建复合转换
*通过在“空”方式下自己编写它们。
现在没有什么东西在搜索之前使用规则了减少操作。

接受和/或返回入口参数保持 key-value 关联。它们可能是有用的例如
*找到最有价值的key ,注意“平原”条目可以使用 {@code newAbstractMap.SimpleEntry(k,v)

Bulk操作.可能突然完成,投掷一个应用程序中遇到的异常函数。在处理其他例外情况时要牢记
*同时执行函数也可以抛出例外情况,或者如果第一个例外没有发生。

平行于顺序形式的加速是常见的但不能保证。涉及简短功能的并行操作
*小地图可能比顺序窗体执行得慢一些。如果
并行计算的基础工作更昂贵比计算本身。类似地,并行化可能不
如果所有的处理器都很忙,就会导致很多实际的并行性执行无关的任务。
所有任务方法的所有参数必须是非空的。

其他(不太懂)

通常充当一个装箱(散列)哈希表,各键值映射被保存在节点中。大多数节点都是实例。具有 hash, key, value, and next字段。然而,存在各种亚类。如
TreeNodes 排列在平衡树中,而不是列表
TreeBins 持有TreeNodes sets的roots
ForwardingNodes 放置在二叉树的头上在resizing。
ReservationNodes 被用作占位符在调用computeIfAbsent 相关方法缺失时建立值

TreeBin, ForwardingNode, andReservationNode 保存普通用户key value 或者hashes
在搜索过程中容易区分因为它们具有egative hash fields and null key and value fields
这些特殊节点既不常见也不短暂,携带一些未使用的字段的影响是微不足道。

ConcurrentHashMap在第一次插入的时候被初始化为power-of-two size 。
。表中的每个bin通常包含一个节点列表(通常情况下,列表仅为零或一个节点)。表访问需要
volatile/atomic reads, writes, and CASes。因为没有别的办法来安排添加进一步的间接,我们使用
本质(sun.misc.Unsafe) operations

使用节点哈希字段的顶部(符号)位进行控制目的。无论如何,它是可利用的,因为寻址
*约束条件。具有负散列字段的节点是专门的在地图方法中处理或忽略。

*第一节点中的插入空仓(通过放置或其变体)是通过 CASing it to the bin中进行的
这是目前大多数key/hash 分布情况下最常用的put操作。其他更新操作(插入)
*删除和替换需要锁
我们不想浪费将一个独立的锁定对象关联到每个bin空间。因此,使用bin列表本身的第一个节点作为锁。这些锁的锁定支持依赖于构建“同步”监视器。

使用列表的第一个节点作为锁本身不足够。
当一个节点被锁定时,任何更新必须首先验证它仍然是锁定后的第一个节点。
以及如果不重试。因为新节点总是被添加到列表中,

一旦一个节点首次在一个bin中,它保持第一个直到被删除或bin变得无效(在调整大小)。

每个BIN锁的主要缺点是其他更新对相同列表保护的bin列表中其他节点的操作锁定可以停顿

例如当用户均等(或)映射时功能需要很长时间、然而,统计上,在随机哈希代码,这不是一个常见的问题。理想情况下,bins 中节点的频率服从泊松分布。
参数大小约为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

两个线程访问不同的锁争用概率,*元素在随机散列下大致为1 / (8 * #elements)

*实践中遇到的实际散列代码分布有时明显偏离均匀的随机性,这个
*包含n>1(<30)的情况,因此某些键必须发生碰撞类似于哑或敌意用法

其中多个键是设计为具有相同的散列码或仅仅在隐藏在高位的时候不同,
所以我们使用二级策略:
当bin中的节点数超过阈值,这些树集使用平衡树来保存节点(A)红色黑树的特殊形式
边界搜索时间到O(log n)、树上的每个搜索步骤至少是常规列表两倍。但假定n不能超过
*(1<64在 用完地址之前。边界搜索步骤,锁定保持时间等,以合理的常数(粗略) 100个节点检查每个操作最坏情况)只要键是可比的(非常常见的字符串,长等)。
* TreeBin节点(树节点)也保持相同的“下一个”遍历指针作为常规节点,因此可以遍历迭代器以相同的方式。
当占用超过一定百分比时,调整表的大小。阈值(名义上,0.75,但见下文)。任何线程注意一个满仓可能有助于调整后的大小。初始化线程分配并设置替换数组、然而,不是拖延,这些其他线程可以继续进行插入等操作
的使用屏蔽我们从在调整大小时过度填充的最坏情况效应。
通过一个接一个地转移箱来调整收益从table到下一张table
但是,线程要求小*索引块(通过字段转移索引),这样做,减少争用
A generation stamp in field
* sizeCtl 尺寸控制确保不重排
因为我们是使用两个扩展的幂,每个bin的元素必须要么停留在相同的索引上,要么以两个幂偏移量移动
通过捕捉来消除不必要的节点创建旧节点可以重复使用的情况,因为它们的下一个字段不会改变。
只有大约六分之一的需要克隆当一个table复制。它们替换的节点将是垃圾一旦被它们引用就可以收集

支持检索和完全并发的哈希表,对于update的高期望并发性。服从 {@link java.util.Hashtable}相同的功能规范。包括对应于每种{@code Hashtable}.方法的方法的版本。但是,即使所有操作都是
线程安全,检索操作做不需要锁定,有< EM >未< /EM>锁定整个表的任何支持,以阻止所有访问的方式。这个类完全依赖于它的程序中的{@code Hashtable} 互操作线程安全,但不在其同步细节。

检索操作(包括{@get})一般不阻塞,因此可能与更新操作重叠,包括@code put}
* and {@code remove}。检索结果反映了最多的结果。*最近完成更新操作。
* 更正式地说,给定key的更新操作包含happens-before与任何(非空)检索之间的关系,用于聚合操作的报告最新值的key . 对于聚合操作{@code putAll} and {@code clear}, 并发检索可能只反映某些条目的插入或删除。同样地, Iterators, Spliterators and Enumerations 返回元素反映hash table 的状态。在创建* iterator/enumeration时或创建后的某个点。他们不throw {@link
* java.util.ConcurrentModificationException ConcurrentModificationException}.。
然而,迭代器被设计成每次只能由一个线程使用。记住汇总状态方法的结果,包括 {@code size}, {@code isEmpty}, and {@code containsValue} 。通常为仅当map没有在其他线程中同时更新时才有用。
*这些方法的结果反映了瞬态状态。对于监测或估计的目的可能是足够的,但不是用于程序控制。

当有太多的表时,该表被动态扩展冲突具有不同哈希代码但掉入的key.相同的槽模表大小,与预期的平均值.每个映射保持大约两个容器的效果(对应)调整大小的0.75个负载因子阈值。可能有很多。
*在添加和移除映射时,围绕此平均值的方差。总体上,这保持了一个普遍接受的时间/空间权衡的哈希表。但是,调整此大小或任何其他类型的散列表可能是相对较慢的操作。如果可能的话,它是
提供一个大小估计作为可选的 {@code initialCapacity} 构造函数参数。附加选项。{@code loadFactor} 构造函数参数提供了另一种方法 通过指定表密度自定义初始表容量。用于计算分配的空间量给定数量的元素。
此外,与以前的兼容性此类的版本,构造函数可以可选地指定期望 {@code concurrencyLevel} 作为附加提示内部大小。注意,使用多个完全相同 {@code hashCode()}的键是降低任何性能的可靠方法。要改善影响,当keys是 {@link Comparable},该类可以使用键之间的比较顺序来帮助破坏关系。

一个 {@link Set} 的ConcurrentHashMap 的投射可以被创造通过 (using {@link #newKeySet()} or {@link #newKeySet(int)}),仅使用键时,使用映射值(可能是暂时性的)未使用或全部采用或者所有操作同样的映射值。

ConcurrentHashMap 可以用作可伸缩的频率map 一种直方图或者多集的形式。使用 {@link
* java.util.concurrent.atomic.LongAdder} 值或者初始化通过{@link #computeIfAbsent computeIfAbsent}.
* 例如,添加计数到a {@code ConcurrentHashMap

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值