HashTable与ConcurrentHashMap的区别

HashTable比ConcurrentHashMap低效,它的实现基本就是给put、get、size等方法加了synchronized,所以并发操作的时候就需要去竞争一把锁,一个线程进行同步操作时,其它线程只能等待。一般不适用于高并发情况下。

ConcurrentHashMap分为JDK1.8之前与JDK1.8之后,

早期的ConcurrentHashMap 实现基于分离锁与volatile

分离锁:将内部进行分段,里面是HashEntry数组

HashEntry内部用volatile修饰Value来实现可见性

构造时,Segment的数量为默认为16,也可以在构造中自定义,和HashMap一样,必须为2^n,如果输入15,会自动调整为16

ConcurrentHashMap会获取递归锁来保证数据的一致性,因为Segment本身就是基于ReentrantLock来扩展实现的,在并发修改的时候,相应的Segment是被锁定的。

在计算所有的Segment的时候,如果不进行同步,会产生并发put问题,可能会让结果不准确,如果直接锁定所有的Segment来进行计算,所需要的代价会非常昂贵,所以ConcurrentHashMap用了重试机制来试图获取可靠值,如果没有监控到发生变化就会直接返回,如果有变化就会获取锁进行操作。

put加锁

通过分段加锁segment,一个hashmap里有若干个segment,每个segment里有若干个桶,桶里存放K-V形式的链表,put数据时通过key哈希得到该元素要添加到的segment,然后对segment进行加锁,然后在哈希,计算得到给元素要添加到的桶,然后遍历桶中的链表,替换或新增节点到桶中

size

分段计算两次,两次结果相同则返回,否则对所以段加锁重新计算

JDK1.8

put CAS 加锁

1.8中不依赖与segment加锁,segment数量与桶数量一致;

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

对key hash计算得到该key存放的桶位置,判断该桶是否为空,为空则利用CAS设置新节点

否则使用synchronize加锁,遍历桶中数据,替换或新增加点到桶中

最后判断是否需要转为红黑树,转换之前判断是否需要扩容

size

利用LongAdd累加计算

猜你喜欢

转载自blog.csdn.net/OrangeRawNorthland/article/details/84262674
今日推荐