美文网首页
JDK并发工具类源码--ConcurrentHashMap

JDK并发工具类源码--ConcurrentHashMap

作者: shoulda | 来源:发表于2018-06-27 10:15 被阅读0次

    1.类的定义

    public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable
    

    由上面可以看出,ConcurrentHashMap是实现了ConcurrentMap接口。ConcurrentMap接口定义可支持并发,NavigableMap接口定义可支持导航,sortedMap接口定义可支持排序。

    2.类的结构

    ConcurrentHashMap内部包含了多个内部类,其中最重要的就是Segment和HashEntry类。

    HashEntry

    HashEntry同HashMap中的Entry一样,每个HashEntry是一个节点,保持key,value,和下一个节点。

    Segment

    Segment是ConcurrentHashMap非常重要的内部类,ConcurrentHashMap保持了一个Segment数组,所有的数据都保存在segment中。ConcurrentHashMap通过N个Segment把数据分成N块,而每块之间互不影响。所以理论上可以同时并行的执行N个加锁的操作,这就是ConcurrentHashMap并发的基础。

    3.构造函数

    public ConcurrentHashMap(int initialCapacity, float loadFactor) {
            this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
        }
    public ConcurrentHashMap(int initialCapacity) {
            this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
        }
    public ConcurrentHashMap() {
            this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
        }
    

    DEFAULT_INITIAL_CAPACITY:默认初始容量
    DEFAULT_LOAD_FACTOR:默认加载因子
    DEFAULT_CONCURRENCY_LEVEL:表示有几个segment

    3.1put(K,V)

    public V put(K key, V value) {
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key.hashCode());
        return segmentFor(hash).put(key, hash, value, false);
    }
    

    ConcurrentHashMap的put方法内部只是根据key的hash值找到对应的Segment.

    V put(K key, int hash, V value, boolean onlyIfAbsent) {
        lock();
        try {
            int c = count;
            if (c++ > threshold) // ensure capacity
                rehash();
            HashEntry<K,V>[] tab = table;
            int index = hash & (tab.length - 1);
            HashEntry<K,V> first = tab[index];
            HashEntry<K,V> e = first;
            while (e != null && (e.hash != hash || !key.equals(e.key)))
                e = e.next;
    
            V oldValue;
            if (e != null) {
                oldValue = e.value;
                if (!onlyIfAbsent)
                    e.value = value;
            }
            else {
                oldValue = null;
                ++modCount;
                tab[index] = new HashEntry<K,V>(key, hash, first, value);
                count = c; // write-volatile
            }
            return oldValue;
        } finally {
            unlock();
        }
    }   
    

    可以看出首先是要加锁;然后读取count值,count记录着segment中元素HashEntry的个数,用volatile修饰的;接着判断增加之后元数数量是否超过阈值,如果超过的话提前扩容。

    3.2remove(Object)

    public V remove(Object key) {
        int hash = hash(key.hashCode());
        return segmentFor(hash).remove(key, hash, null);
    }
    
    /**
     * Remove; match on key only if value null, else match both.
     */
    V remove(Object key, int hash, Object value) {
        //由于remove是结构性修改,所以第一步便是lock
        lock();
        try {
            //读取count值,此处是利用volatile变量的内存可见性来保证读线程能够及时的读取到最新值(后面会单独介绍)
            int c = count - 1;
            //是根据key的hashCode找到该节点对应的桶
            HashEntry<K,V>[] tab = table;
            int index = hash & (tab.length - 1);
            HashEntry<K,V> first = tab[index];
            HashEntry<K,V> e = first;
            //循环找到该节点
            while (e != null && (e.hash != hash || !key.equals(e.key)))
                e = e.next;
    
            V oldValue = null;
            if (e != null) {
            //找到待删除节点
                V v = e.value;
                //如果value==null,则无需关心节点的值是否与指定值相同,否则只有在两者相同情况才可删除
                if (value == null || value.equals(v)) {
                    oldValue = v;
                    // All entries following removed node can stay
                    // in list, but all preceding ones need to be
                    // cloned.
                    ++modCount;
                    HashEntry<K,V> newFirst = e.next;
                    for (HashEntry<K,V> p = first; p != e; p = p.next)
                        newFirst = new HashEntry<K,V>(p.key, p.hash,
                                                      newFirst, p.value);
                    tab[index] = newFirst;
                    count = c; // write-volatile
                }
            }
            return oldValue;
        } finally {
            unlock();
        }
    }
    

    在删除一个节点时,为了不影响正在遍历链表的线程,采用复制方式,而并不是直接移除带删除点的结点。

    3.3get(Object)

    public V get(Object key) {
        int hash = hash(key.hashCode());
        return segmentFor(hash).get(key, hash);
    }
    

    ConcurrentHashMap的get()方法和put()方法一样,都依赖于Segment的get()方法。

     V get(Object key, int hash) {
        if (count != 0) { // read-volatile
            HashEntry<K,V> e = getFirst(hash);
            while (e != null) {
                if (e.hash == hash && key.equals(e.key)) {
                    V v = e.value;
                    if (v != null)
                        return v;
                    return readValueUnderLock(e); // recheck
                }
                e = e.next;
            }
        }
        return null;
    }
    
     /**
     * Returns properly casted first entry of bin for given hash.
      */
     HashEntry<K,V> getFirst(int hash) {
         HashEntry<K,V>[] tab = table;
         return tab[hash & (tab.length - 1)];
     }
    
    /**
     * Reads value field of an entry under lock. Called if value
     * field ever appears to be null. This is possible only if a
     * compiler happens to reorder a HashEntry initialization with
     * its table assignment, which is legal under memory model
     * but is not known to ever occur.
     */
    V readValueUnderLock(HashEntry<K,V> e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }
    

    相关文章

      网友评论

          本文标题:JDK并发工具类源码--ConcurrentHashMap

          本文链接:https://www.haomeiwen.com/subject/inlmyftx.html