Skip to content

Commit d11cfa5

Browse files
committed
[docs update]完善补充HashMap、并发集合相关的内容
1 parent edbea01 commit d11cfa5

File tree

4 files changed

+25
-6
lines changed

4 files changed

+25
-6
lines changed

docs/database/redis/redis-data-structures-02.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ HyperLogLog 相关的命令非常少,最常用的也就 3 个。
125125

126126
**数量巨大(百万、千万级别以上)的计数场景**
127127

128-
- 举例:热门网站每日/每周/每月访问 ip 数统计、热门帖子 uv 统计
128+
- 举例:热门网站每日/每周/每月访问 ip 数统计、热门帖子 uv 统计
129129
- 相关命令:`PFADD``PFCOUNT`
130130

131131
## Geospatial (地理位置)

docs/database/redis/redis-questions-01.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ Redis 从 4.0 版本开始,支持通过 Module 来扩展其功能以满足特
146146
- **消息队列**:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
147147
- **延时队列**:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
148148
- **分布式 Session** :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
149-
- **复杂业务场景**:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。
149+
- **复杂业务场景**:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜、通过 HyperLogLog 统计网站 UV 和 PV
150150
- ……
151151

152152
### 如何基于 Redis 实现分布式锁?

docs/java/collection/java-collection-questions-02.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,23 @@ static int hash(int h) {
222222

223223
#### JDK1.8 之后
224224

225-
相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
225+
相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树。
226+
227+
这样做的目的是减少搜索时间:链表的查询效率为 O(n)(n 是链表的长度),红黑树是一种自平衡二叉搜索树,其查询效率为 O(log n)。当链表较短时,O(n) 和 O(log n) 的性能差异不明显。但当链表变长时,查询性能会显著下降。
226228

227229
![jdk1.8之后的内部结构-HashMap](https://oss.javaguide.cn/github/javaguide/java/collection/jdk1.8_hashmap.png)
228230

231+
**为什么优先扩容而非直接转为红黑树?**
232+
233+
数组扩容能减少哈希冲突的发生概率(即将元素重新分散到新的、更大的数组中),这在多数情况下比直接转换为红黑树更高效。
234+
235+
红黑树需要保持自平衡,维护成本较高。并且,过早引入红黑树反而会增加复杂度。
236+
237+
**为什么选择阈值 8 和 64?**
238+
239+
1. 泊松分布表明,链表长度达到 8 的概率极低(小于千万分之一)。在绝大多数情况下,链表长度都不会超过 8。阈值设置为 8,可以保证性能和空间效率的平衡。
240+
2. 数组长度阈值 64 同样是经过实践验证的经验值。在小数组中扩容成本低,优先扩容可以避免过早引入红黑树。数组大小达到 64 时,冲突概率较高,此时红黑树的性能优势开始显现。
241+
229242
> TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
230243
231244
我们来结合源码分析一下 `HashMap` 链表到红黑树的转换。

docs/java/concurrent/java-concurrent-collections.md

+9-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@ JDK 提供的这些容器大部分在 `java.util.concurrent` 包中。
1515

1616
## ConcurrentHashMap
1717

18-
我们知道 `HashMap` 不是线程安全的,在并发场景下如果要保证一种可行的方式是使用 `Collections.synchronizedMap()` 方法来包装我们的 `HashMap`。但这是通过使用一个全局的锁来同步不同线程间的并发访问,因此会带来不可忽视的性能问题
18+
我们知道`HashMap` 是线程不安全的,如果在并发场景下使用,一种常见的解决方式是通过 `Collections.synchronizedMap()` 方法对 `HashMap` 进行包装,使其变为线程安全。不过,这种方式是通过一个全局锁来同步不同线程间的并发访问,会导致严重的性能瓶颈,尤其是在高并发场景下
1919

20-
所以就有了 `HashMap` 的线程安全版本—— `ConcurrentHashMap` 的诞生
20+
为了解决这一问题,`ConcurrentHashMap` 应运而生,作为 `HashMap` 的线程安全版本,它提供了更高效的并发处理能力
2121

2222
在 JDK1.7 的时候,`ConcurrentHashMap` 对整个桶数组进行了分割分段(`Segment`,分段锁),每一把锁只锁容器其中一部分数据(下面有示意图),多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
2323

24-
到了 JDK1.8 的时候,`ConcurrentHashMap` 已经摒弃了 `Segment` 的概念,而是直接用 `Node` 数组+链表+红黑树的数据结构来实现,并发控制使用 `synchronized` 和 CAS 来操作。(JDK1.6 以后 `synchronized` 锁做了很多优化) 整个看起来就像是优化过且线程安全的 `HashMap`,虽然在 JDK1.8 中还能看到 `Segment` 的数据结构,但是已经简化了属性,只是为了兼容旧版本。
24+
![Java7 ConcurrentHashMap 存储结构](https://oss.javaguide.cn/github/javaguide/java/collection/java7_concurrenthashmap.png)
25+
26+
到了 JDK1.8 的时候,`ConcurrentHashMap` 取消了 `Segment` 分段锁,采用 `Node + CAS + synchronized` 来保证并发安全。数据结构跟 `HashMap` 1.8 的结构类似,数组+链表/红黑二叉树。Java 8 在链表长度超过一定阈值(8)时将链表(寻址时间复杂度为 O(N))转换为红黑树(寻址时间复杂度为 O(log(N)))。
27+
28+
Java 8 中,锁粒度更细,`synchronized` 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,就不会影响其他 Node 的读写,效率大幅提升。
29+
30+
![Java8 ConcurrentHashMap 存储结构](https://oss.javaguide.cn/github/javaguide/java/collection/java8_concurrenthashmap.png)
2531

2632
关于 `ConcurrentHashMap` 的详细介绍,请看我写的这篇文章:[`ConcurrentHashMap` 源码分析](./../collection/concurrent-hash-map-source-code.md)
2733

0 commit comments

Comments
 (0)