设为首页 加入收藏

TOP

ConcurrentHashMap 源码分析(一)
2015-11-21 00:57:35 来源: 作者: 【 】 浏览:5
Tags:ConcurrentHashMap 源码 分析

CocurrentHashMap 作用

HashTable通过对整张表加锁的方式实现并发hash查找与储存,CocurrentHashMapt通过Segment的方式可以实现相同的功能,不过效率更加高,在jdk1.6的时候,CocuentHashMap有弱一致性的问题,不过在jdk1.7的时候,这个问题已经修复了。所以是并发安全性还是性能都是非常高的。接下来我尝试基于jdk1.7的 源码去分析CocurrentHashMap。

cocurrentHashMap 初始化预处理

    // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long SBASE;
    private static final int SSHIFT;
    private static final long TBASE;
    private static final int TSHIFT;

    static {
        int ss, ts;
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class
   tc = HashEntry[].class;
            Class
   sc = Segment[].class;
            TBASE = UNSAFE.arrayBaseOffset(tc);
            SBASE = UNSAFE.arrayBaseOffset(sc);
            ts = UNSAFE.arrayIndexScale(tc);
            ss = UNSAFE.arrayIndexScale(sc);
        } catch (Exception e) {
            throw new Error(e);
        }
        if ((ss & (ss-1)) != 0 || (ts & (ts-1)) != 0)
            throw new Error(data type scale not a power of two);
        SSHIFT = 31 - Integer.numberOfLeadingZeros(ss);
        TSHIFT = 31 - Integer.numberOfLeadingZeros(ts);
    }
代码解析:首先获取Unsafe提供cas操作,java底层多线程并发都是通过cas完成的,不过cas操作对于高精度的并发还是存在一定问题。【至于这个问题,以后再分析】。UNSAFE.arrayBaseOffset(tc)和UNSAFE.arrayBaseOffset(sc)这两个都是用于计算HashEntry和Segment实体对象相对于数组对象的内存偏移值。这是cas操作必须要获取的值。 注释:
//获取数组中第一个元素的偏移量(get offset of a first element in the array)  
public native int arrayBaseOffset(java.lang.Class aClass);  
//获取数组中一个元素的大小(get size of an element in the array)  
public native int arrayIndexScale(java.lang.Class aClass); 


cocurrentHashMap 初始化

    public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (concurrencyLevel > MAX_SEGMENTS)
            concurrencyLevel = MAX_SEGMENTS;
        // Find power-of-two sizes best matching arguments
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;
        }
        this.segmentShift = 32 - sshift;
        this.segmentMask = ssize - 1;
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        int c = initialCapacity / ssize;
        if (c * ssize < initialCapacity)
            ++c;
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        while (cap < c)
            cap <<= 1;
        // create segments and segments[0]
        Segment
  
    s0 =
            new Segment
   
    (loadFactor, (int)(cap * loadFactor), (HashEntry
    
     [])new HashEntry
     [cap]); Segment
     
      [] ss = (Segment
      
       [])new Segment
       [ssize]; UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0] this.segments = ss; }
      
     
    
   
  

代码解析:以上代码主要的作用是初始化Segment[]数组对象以及Segment对象。解析来分析最重要的put和get方法。

put方法

    public V put(K key, V value) {
        Segment
   
     s; if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); int j = (hash >>> segmentShift) & segmentMask; if ((s = (Segment
    
     )UNSAFE.getObject // nonvolatile; recheck (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment s = ensureSegment(j); return s.put(key, hash, value, false); }
    
   

代码解析:大体的意思是先计算出key的hash值,然后利用这个hash值得到Segment对象。然后Segment对象执行put方法。这样就完成了put操作。由于这个过程非常重要,我们肯定想要知道它是如何处理并发以及 内部实现。

ensureSegment

    private Segment
  
    ensureSegment(int k) {
        final Segment
   
    [] ss = this.segments; long u = (k << SSHIFT) + SBASE; // raw offset Segment
    
      seg; if ((seg = (Segment
     
      )UNSAFE.getObjectVolatile(ss, u)) == null) { Segment
      
        proto = ss[0]; // use segment 0 as prototype int cap = proto.table.length; float lf = proto.loadFactor; int threshold = (int)(cap * lf); HashEntry
       
        [] tab = (HashEntry
        
         [])new HashEntry
         [cap]; if ((seg = (Segment
         
          )UNSAFE.getObjectVolatile(ss, u)) == null) { // recheck Segment
          
            s = new Segment
           
            (lf, threshold, tab); while ((seg = (Segment
            
             )UNSAFE.getObjectVolatile(ss, u)) == null) { if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s)) break; } } } return seg; }
            
           
          
         
        
       
      
     
    
   
  

首先计算出偏移量,然后利用UnSafe去获取对象。在这里有可能大家对这个偏移值的获取有点疑惑,在这里我也分析一下这个偏移量获取既long u=(k<

segment->put

        final V put(K key, int hash, V va
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C++再次理解虚表 下一篇hihoCoder - 1121 - 二分图判定

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: