File tree 4 files changed +25
-6
lines changed
4 files changed +25
-6
lines changed Original file line number Diff line number Diff line change @@ -125,7 +125,7 @@ HyperLogLog 相关的命令非常少,最常用的也就 3 个。
125
125
126
126
** 数量巨大(百万、千万级别以上)的计数场景**
127
127
128
- - 举例:热门网站每日/每周/每月访问 ip 数统计、热门帖子 uv 统计、
128
+ - 举例:热门网站每日/每周/每月访问 ip 数统计、热门帖子 uv 统计。
129
129
- 相关命令:` PFADD ` 、` PFCOUNT ` 。
130
130
131
131
## Geospatial (地理位置)
Original file line number Diff line number Diff line change @@ -146,7 +146,7 @@ Redis 从 4.0 版本开始,支持通过 Module 来扩展其功能以满足特
146
146
- ** 消息队列** :Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
147
147
- ** 延时队列** :Redisson 内置了延时队列(基于 Sorted Set 实现的)。
148
148
- ** 分布式 Session** :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
149
- - ** 复杂业务场景** :通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。
149
+ - ** 复杂业务场景** :通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜、通过 HyperLogLog 统计网站 UV 和 PV 。
150
150
- ……
151
151
152
152
### 如何基于 Redis 实现分布式锁?
Original file line number Diff line number Diff line change @@ -222,10 +222,23 @@ static int hash(int h) {
222
222
223
223
#### JDK1.8 之后
224
224
225
- 相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
225
+ 相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树。
226
+
227
+ 这样做的目的是减少搜索时间:链表的查询效率为 O(n)(n 是链表的长度),红黑树是一种自平衡二叉搜索树,其查询效率为 O(log n)。当链表较短时,O(n) 和 O(log n) 的性能差异不明显。但当链表变长时,查询性能会显著下降。
226
228
227
229
![ jdk1.8之后的内部结构-HashMap] ( https://oss.javaguide.cn/github/javaguide/java/collection/jdk1.8_hashmap.png )
228
230
231
+ ** 为什么优先扩容而非直接转为红黑树?**
232
+
233
+ 数组扩容能减少哈希冲突的发生概率(即将元素重新分散到新的、更大的数组中),这在多数情况下比直接转换为红黑树更高效。
234
+
235
+ 红黑树需要保持自平衡,维护成本较高。并且,过早引入红黑树反而会增加复杂度。
236
+
237
+ ** 为什么选择阈值 8 和 64?**
238
+
239
+ 1 . 泊松分布表明,链表长度达到 8 的概率极低(小于千万分之一)。在绝大多数情况下,链表长度都不会超过 8。阈值设置为 8,可以保证性能和空间效率的平衡。
240
+ 2 . 数组长度阈值 64 同样是经过实践验证的经验值。在小数组中扩容成本低,优先扩容可以避免过早引入红黑树。数组大小达到 64 时,冲突概率较高,此时红黑树的性能优势开始显现。
241
+
229
242
> TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
230
243
231
244
我们来结合源码分析一下 ` HashMap ` 链表到红黑树的转换。
Original file line number Diff line number Diff line change @@ -15,13 +15,19 @@ JDK 提供的这些容器大部分在 `java.util.concurrent` 包中。
15
15
16
16
## ConcurrentHashMap
17
17
18
- 我们知道 ` HashMap ` 不是线程安全的,在并发场景下如果要保证一种可行的方式是使用 ` Collections.synchronizedMap() ` 方法来包装我们的 ` HashMap ` 。但这是通过使用一个全局的锁来同步不同线程间的并发访问,因此会带来不可忽视的性能问题 。
18
+ 我们知道, ` HashMap ` 是线程不安全的,如果在并发场景下使用,一种常见的解决方式是通过 ` Collections.synchronizedMap() ` 方法对 ` HashMap ` 进行包装,使其变为线程安全。不过,这种方式是通过一个全局锁来同步不同线程间的并发访问,会导致严重的性能瓶颈,尤其是在高并发场景下 。
19
19
20
- 所以就有了 ` HashMap ` 的线程安全版本—— ` ConcurrentHashMap ` 的诞生 。
20
+ 为了解决这一问题, ` ConcurrentHashMap ` 应运而生,作为 ` HashMap ` 的线程安全版本,它提供了更高效的并发处理能力 。
21
21
22
22
在 JDK1.7 的时候,` ConcurrentHashMap ` 对整个桶数组进行了分割分段(` Segment ` ,分段锁),每一把锁只锁容器其中一部分数据(下面有示意图),多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
23
23
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 )
25
31
26
32
关于 ` ConcurrentHashMap ` 的详细介绍,请看我写的这篇文章:[ ` ConcurrentHashMap ` 源码分析] ( ./../collection/concurrent-hash-map-source-code.md ) 。
27
33
You can’t perform that action at this time.
0 commit comments