🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### JDK1.7 ConcurrentHashMap结构 JDK1.7 ConcurrentHashMap 数据结构如下所示: ![](https://img.kancloud.cn/85/16/85169461baead59a624a223b3406c1d1_1370x850.png) Segament 是一个ConcurrentHashMap内部类,底层结构与 HashMap 一致。另外Segament 继承自 ReentrantLock。 当新元素加入 ConcurrentHashMap 时,首先根据 key hash 值找到相应的 Segament。接着直接对 Segament 上锁,若获取成功,后续操作步骤如同 HashMap。 由于锁的存在,Segament 内部操作都是并发安全,同时由于其他 Segament 未被占用,因此可以支持 concurrencyLevel 个线程安全的并发读写 #### size统计问题 虽然 ConcurrentHashMap 引入分段锁解决多线程并发的问题,但是同时引入新的复杂度,导致计算 ConcurrentHashMap 元素数量将会变得复杂。 由于 ConcurrentHashMap 元素实际分布在 Segament 中,为了统计实际数量,只能遍历 Segament数组求和。 为了数据的准确性,这个过程过我们需要锁住所有的 Segament,计算结束之后,再依次解锁。不过这样做,将会导致写操作被阻塞,一定程度降低 ConcurrentHashMap性能。 所以这里对 ConcurrentHashMap#size 统计方法进行一定的优化。 Segment 每次被修改(写入,删除),都会对 modCount(更新次数)加 1。只要相邻两次计算获取所有的 Segment modCount 总和一致,则代表两次计算过程并无写入或删除,可以直接返回统计数量。 如果三次计算结果都不一致,那没办法只能对所有 Segment 加锁,重新计算结果。 这里需要注意的是,这里求得 size 数量不能做到 100% 准确。这是因为最后依次对 Segment 解锁后,可能会有其他线程进入写入操作。这样就导致返回时的数量与实际数不一致。 不过这也能被接受,总不能因为为了统计元素停止所有元素的写入操作 #### 性能问题 极端情况下所有写入都落在同一个`Segment`中,这就导致`ConcurrentHashMap`退化成`SynchronizedMap`,共同抢一把锁