分析HashTableHashMap与ConcurrentHashMap

Posted dasdfdfecvcx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分析HashTableHashMap与ConcurrentHashMap相关的知识,希望对你有一定的参考价值。

一、HashTable、HashMap有什么不同

1.1 HashTable

HashTable是遗留类,很多映射的常用功能与HashMap类似,不同的是它继承自Dictionary类不支持null键和值,并且是线程安全的,任意时间只有一个线程能写HashTable,锁的粒度粗,并发性不如ConcurrentHashMap,因为其引入了分段锁,每次只将一个小部分上锁

1.2 HashMap(数组+链表+红黑树)

HashMap不是同步的,最多只允许一条记录的键位null,允许多条记录的值为null,它是根据键的hashCode值定位到数组对应的存储位置,通常情况下HashMap进行put或get的操作可以达到常数的时间性能

1.2.1 HashMap java 1.7的实现


数组内每个元素是单向链表,绿色的实体是Entry的实例,hashcode值相同的元素以单链表的形式串在一起

capacity :当前数组容量,始终保持2^n ,可以扩容,扩容后数组大小为当前的2倍
loadFactor : 负载因子,默认为0.75
threshold :扩容的阈值 , 等于capacity * loadFactor
1.2.2 HashMap java 1.8 的实现

在1.8中利用了红黑树对链表进行优化,关于红黑树可以参考这个网站,将原理以动画的形式呈现 :
https://www.cs.usfca.edu/~galles/visualization/RedBlack.html

查询数据需要遍历链表,时间复杂度取决于链表的长度为O(N), 红黑树的查询时间复杂度为O(logN),查询的时间复杂度降低了

当链表大小超过阈值(TREEIFY_THRESHOLD, 8),链表就会被改造为红黑树

为什么HashMap要树化 :本质上是安全问题,因为在元素放置过程中,如果一个对象哈希冲突,都被放置到一个桶里,则会形成一个链表,影响存取性能,而在现实中,构造哈希冲突的数据并不是非常复杂的事情,恶意代码就可以利用这些数据,大量与服务器交互,导致服务器端CPU大量占用,这就构成了哈希碰撞拒绝服务攻击

二、ConcurrentHashMap

2.1 为什么需要ConcurrentHashMap

HashTable比较低效,它同步的实现是将put,get,size等各种方法加上“Synchronize”,这就导致了所有并发操作都要竞争同一把锁,一个线程在进行同步操作时,其他线程只能等待,大大降低了并发操作的效率。

而HashMap不是线程安全的,并发情况会导致类似CPU占用100%问题

2.2 实现方式1.7

分离锁
也就是将内部进行分段(Segment),Segment通过继承ReentrantLock来进行加锁,在进行并发操作的时候,只需要锁定相应Segment,有效避免了类似的HashTable锁定整体的同步问题了,里面则是HashEntry的数组和HashMap类似,哈希相同的条目也是以链表形式存放的

HashEntry
使用volatile的value字段来保证可见性

并行度
concurrencyLevel默认是16,Java 需要它是 2 的幂数值,如果输入是类似 15 这种非幂值,会被自动调整到 16 之类 2 的幂数值,

ConcurrentHashMap默认有16个Segment,最多可同时支持16个线程并发写,只要它们操作分布在不同的Segment上

 

2.2 实现方式1.8(红黑树)

变化

与HashMap内部结构非常相似,但是同步粒度更细致一些


内部仍有Segment的定义,但仅仅为了保证序列化时的兼容性而已,不再有结构上的用处

初始化操作简化,修改为lazy-load形式,有效避免初始开销

lazy-load”也被叫作“延迟价值”,它的核心思想是把对象的实例化延迟到真正调用该对象的时候,这样做的好处是可以减轻大量对象在实例化时对资源的消耗,而不是在程序初始化的时候就预先将对象实例化

数据存储利用volatile来保证可见性

使用CAS等操作,在特定场景进行无锁并发操作

使用Unsafe , LongAddr 之类底层手段进行极端情况的优化

并发操作

首先判断容器是否为空,为空则进行初始化利用volatile的sizeCtl作为互斥手段,如果发现竞争性的初始化,就暂停在那里,等待条件恢复,否则利用CAS设置排他标志(U.compareAndSwapInt(this, SIZECTL, sc, -1));否则重试

对key hash计算得到该key存放的桶位置,判断该桶是否为空,为空则利用CAS设置新节点否则使用synchronize加锁,遍历桶中数据,替换或新增加点到桶中
————————————————
原文链接:https://blog.csdn.net/Dev_Hugh/article/details/106532236

https://www.dianyuan.com/people/836381
https://www.dianyuan.com/people/836382
https://www.dianyuan.com/people/836383
https://www.dianyuan.com/people/836525
https://www.dianyuan.com/people/836526
https://www.dianyuan.com/people/836528

以上是关于分析HashTableHashMap与ConcurrentHashMap的主要内容,如果未能解决你的问题,请参考以下文章

Java之HashtableHashMap及Properties

(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解

go并发

ConcurrentHashMap源码及分析

(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解

greenplum master节点日志报错 ERROR: tuple to be updated was already moved to another segment due to concur