(n - 1) & hash
” 。(n 代表数组长度) 。这也就解释了 HashMap 的长度为什么是 2 的幂次方 。
这个算法应该如何设计呢?
我们首先可能会想到采用%取余的操作来实现 。但是,重点来了:“取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;) 。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是 2 的幂次方
HashMap 多线程操作导致死循环问题主要原因在于并发下的 Rehash 会造成元素之间会形成一个循环链表 。不过 , jdk 1.8 后解决了这个问题,但是还是不建议在多线程下使用 HashMap , 因为多线程下使用 HashMap 还是会存在其他问题比如数据丢失 。并发环境下推荐使用 ConcurrentHashMap
。
ConcurrentHashMap 和 Hashtable 的区别ConcurrentHashMap
和 Hashtable
的区别主要体现在实现线程安全的方式上不同 。
- 底层数据结构: JDK1.7 的
ConcurrentHashMap
底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8
的结构一样,数组+链表/红黑二叉树 。Hashtable
和 JDK1.8 之前的HashMap
的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
- 实现线程安全的方式(重要):
① 在 JDK1.7 的时候,ConcurrentHashMap
(分段锁) 对整个桶数组进行了分割分段(Segment
),每一把锁只锁容器其中一部分数据 , 多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率 。到了 JDK1.8 的时候已经摒弃了Segment
的概念,而是直接用Node
数组+链表+红黑树的数据结构来实现,并发控制使用synchronized
和 CAS 来操作 。(JDK1.6 以后 对synchronized
锁做了很多优化) 整个看起来就像是优化过且线程安全的HashMap
,虽然在 JDK1.8 中还能看到Segment
的数据结构,但是已经简化了属性 , 只是为了兼容旧版本;②Hashtable
(同一把锁) :使用synchronized
来保证线程安全,效率非常低下 。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素 , 另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低 。
Hashtable:
文章插图
https://www.cnblogs.com/chengxiao/p/6842045.html>
JDK1.7 的 ConcurrentHashMap:
文章插图
JDK1.8 的 ConcurrentHashMap:
文章插图
JDK1.8 的
ConcurrentHashMap
不再是 Segment 数组 + HashEntry 数组 + 链表,而是 Node 数组 + 链表 / 红黑树 。不过,Node 只能用于链表的情况,红黑树的情况需要使用 TreeNode
。当冲突链表达到一定长度时,链表会转换成红黑树 。ConcurrentHashMap 线程安全的具体实现方式/底层具体实现JDK1.7(上面有示意图)首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问 。
ConcurrentHashMap
是由 Segment
数组结构和 HashEntry
数组结构组成 。Segment 实现了
ReentrantLock
, 所以 Segment
是一种可重入锁,扮演锁的角色 。HashEntry
用于存储键值对数据 。static class Segment<K , V> extends ReentrantLock implements Serializable {}
一个 ConcurrentHashMap
里包含一个 Segment
数组 。Segment
的结构和 HashMap
类似,是一种数组和链表结构,一个 Segment
包含一个 HashEntry
数组,每个 HashEntry
是一个链表结构的元素,每个 Segment
守护着一个 HashEntry
数组里的元素,当对 HashEntry
数组的数据进行修改时,必须首先获得对应的 Segment
的锁 。JDK1.8 (上面有示意图)
ConcurrentHashMap
取消了 Segment
分段锁 , 采用 CAS
和
推荐阅读
- Java单例模式,看这一篇就够了
- 微信支付v3接口的 官方 Java SDK
- Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化
- 夯实Java基础,一篇文章全解析线程问题
- 四 Java多线程-ThreadPool线程池-2
- 生成器函数 javascript异步编程之generator与asnyc/await语法糖
- Java Timer使用介绍
- 三 Java多线程-ThreadPool线程池
- 二 Java多线程-线程关键字
- 二 Java 编码那些事