JDK并发工具类源码--ConcurrentHashMap

1.类的定义

public class ConcurrentHashMap extends AbstractMap implements ConcurrentMap, 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[] tab = table;
        int index = hash & (tab.length - 1);
        HashEntry first = tab[index];
        HashEntry 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(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[] tab = table;
        int index = hash & (tab.length - 1);
        HashEntry first = tab[index];
        HashEntry 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 newFirst = e.next;
                for (HashEntry p = first; p != e; p = p.next)
                    newFirst = new HashEntry(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 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 getFirst(int hash) {
     HashEntry[] 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 e) {
    lock();
    try {
        return e.value;
    } finally {
        unlock();
    }
}

你可能感兴趣的:(JDK并发工具类源码--ConcurrentHashMap)