zoukankan      html  css  js  c++  java
  • 如何在很短的时间内将大量数据插入到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并发环境下修改的问题(说实话我没看懂-_-!)。

    JDK1.7中扩容中转移数组transfer()方法源码。

    void transfer(Entry[] newTable, boolean rehash) {
        //新table的容量
        int newCapacity = newTable.length;
        //遍历原table
        for (Entry<K,V> e : table) {
            while(null != e) {
                //保存下一次循环的 Entry<K,V>
                Entry<K,V> next = e.next;
                if (rehash) {
                    //通过e的key值计算e的hash值
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                //得到e在新table中的插入位置
                int i = indexFor(e.hash, newCapacity);
                //采用链头插入法将e插入i位置,最后得到的链表相对于原table正好是头尾相反的
                e.next = newTable[i];
                newTable[i] = e;
                //下一次循环
                e = next;
            }
        }
    }


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

  • 相关阅读:
    Python YAML
    Python JASON
    Python正则表达式
    := / ?= /+=
    eclipse的springMVC环境搭建并输出HelloWorld
    SpringMVC简单介绍
    一个简单的MyBatis项目(应用)
    No mapping found for HTTP request with URI [/spring_liu/hello.do] in DispatcherServlet with name 'SpringMVC'
    xml中单词下面有提示下划线
    为什么maven 创建web工程不自动生成Deployment Descriptor:工程名
  • 原文地址:https://www.cnblogs.com/Esquecer/p/11418350.html
Copyright © 2011-2022 走看看