如何在很短的时间内将大量数据插入到ConcurrentHashMap(转)

时间:2019-08-27
本文章向大家介绍如何在很短的时间内将大量数据插入到ConcurrentHashMap(转),主要包括如何在很短的时间内将大量数据插入到ConcurrentHashMap(转)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

将大批量数据保存到map中有两个地方的消耗将会是比较大的:第一个是扩容操作,第二个是锁资源的争夺。第一个扩容的问题,主要还是要通过配置合理的容量大小和扩容因子,尽可能减少扩容事件的发生;第二个锁资源的争夺,在put方法中会使用synchonized对头节点进行加锁,而锁本身也是分等级的,因此我们的主要思路就是尽可能的避免锁等级。所以,针对第二点,我们可以将数据通过通过ConcurrentHashMap的spread方法进行预处理,这样我们可以将存在hash冲突的数据放在一个组里面,每个组都使用单线程进行put操作,这样的话可以保证锁仅停留在偏向锁这个级别,不会升级,从而提升效率。

其他:

ConcurrentHashMap 1.7和1.8的区别
1、整体结构
1.7:Segment + HashEntry + Unsafe

1.8: 移除Segment,使锁的粒度更小,Synchronized + CAS + Node + Unsafe

2、put()
1.7:先定位Segment,再定位桶,put全程加锁,没有获取锁的线程提前找桶的位置,并最多自旋64次获取锁,超过则挂起。

1.8:由于移除了Segment,类似HashMap,可以直接定位到桶,拿到first节点后进行判断,1、为空则CAS插入;2、为-1则说明在扩容,则跟着一起扩容;3、else则加锁put(类似1.7)

3、get()
基本类似,由于value声明为volatile,保证了修改的可见性,因此不需要加锁。

4、resize()
1.7:跟HashMap步骤一样,只不过是搬到单线程中执行,避免了HashMap在1.7中扩容时死循环的问题,保证线程安全。

1.8:支持并发扩容,HashMap扩容在1.8中由头插改为尾插(为了避免死循环问题),ConcurrentHashmap也是,迁移也是从尾部开始,扩容前在桶的头部放置一个hash值为-1的节点,这样别的线程访问时就能判断是否该桶已经被其他线程处理过了。

5、size()
1.7:很经典的思路:计算两次,如果不变则返回计算结果,若不一致,则锁住所有的Segment求和。

1.8:用baseCount来存储当前的节点个数,这就设计到baseCount并发环境下修改的问题(说实话我没看懂-_-!)。


原文链接:https://blog.csdn.net/u013374645/article/details/88700927

原文地址:https://www.cnblogs.com/Esquecer/p/11418350.html