From 377d406629b0617c3620cac7b0fd1808c61fd50f Mon Sep 17 00:00:00 2001 From: Omooo <869759698@qq.com> Date: Wed, 10 Jun 2020 19:53:42 +0800 Subject: [PATCH] =?UTF-8?q?Update=20=E9=9B=86=E5=90=88=E6=BA=90=E7=A0=81.m?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blogs/Java/口水话/集合源码.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/blogs/Java/口水话/集合源码.md b/blogs/Java/口水话/集合源码.md index 1ef201b..0ddb196 100644 --- a/blogs/Java/口水话/集合源码.md +++ b/blogs/Java/口水话/集合源码.md @@ -109,5 +109,15 @@ CopyOnWriteArrayList 读的时候不需要加锁,适合读多写少的场景 #### ConcurrentHashMap -ConcurrentHashMap 是一个线程安全的 HashMap,它是不允许 key 或 value 为 null 的。它的底层也是使用数组 + 链表 + 红黑树来实现的, +ConcurrentHashMap 和 HashMap 相比,它的底层也是使用数组 + 链表 + 红黑树来实现的,不过新增了转移节点(ForwardingNode),是为了保证扩容时的线程安全的节点。而且红黑树结构略有不同,HashMap 的红黑树节点叫做 TreeNode,TreeNode 不仅仅有属性,是维护者红黑树的结构,比如说查找、新增等;ConcurrentHashMao 中红黑树被拆分成两块,TreeNode 仅仅维护属性和查找功能,新增了 TreeBin,来维护红黑树结构,并负责根节点的加锁和解锁。 + +和 HashTable 相比,它也是线程安全的,但是 ConcurrentHashMap 多个线程同时进行 put、remove 等操作时并不会阻塞,可以同时进行;HashTable 在操作时,会锁住整个 Map。 + +ConcurrentHashMap 在 put 时,在一个 for 死循环里面,也就是一定能保证 put 成功。第一次 put 时,会先通过 CAS 保证只有一个线程扩容,如果有其他线程在执行扩容操作,就会调用 Thread.yield 释放当前 CPU 调度权,重新发起锁的竞争,这一步是在一个 while 循环里面去做的只要容量为空,就一直循环。再求出 table 索引之后,如果该槽点为空并不是直接新增,而是通过 CAS 新增。如果判断当前是转移节点(转转移节点的 hash 值固定为 MOVED 为 -1),表示该槽点正在扩容,就会一直等待扩容完成;如果当前槽点有值,就是 key 的 hash 冲突的情况,此时槽点上可能是链表或红黑树,这时候就会通过 synchronized 锁住槽点,来保证同一时刻只会有一个线程能对槽点进行修改。也就是说,put 是通过自旋 + CAS + 锁来实现线程安全的。 + +在进行扩容时,首先需要把老数组的值全部拷贝到新数组上,先从数组的队尾开始拷贝,拷贝数组的槽点时,会先把原数组的槽点,保证原数组槽点不能操作,成功拷贝到新数组时,把原数组槽点赋值为转移节点。如果此时有数据要 put 到此槽点就会一直等待。拷贝时也是扩容原数组两倍大小。 + +ConcurrentHashMap 的 get 和 HashMap 基本无差。 + +####