Android ConcurrentHashMap

128 阅读1分钟

以下内容基于Android Api 28 。

ConcurrentHashMap中,数据实际存储在其内部的Node数组table之中。保存数据时,实际调用putVal(K key, V value, boolean onlyIfAbsent)方法。

```
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node[] tab = table; ; ) {
        Node f;
        int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null,
                    new Node(hash, key, value, null)))
                break;// no lock when adding to empty bin
        } else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        for (Node e = f; ; ++binCount) {
                            K ek;
                            if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                            (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new Node(hash, key,
                                        value, null);
                                break;
                            }
                        }
                    } else if (f instanceof TreeBin) {
                        Node p;
                        binCount = 2;
                        if ((p = ((TreeBin) f).putTreeVal(hash, key, value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    } else if (f instanceof ReservationNode)
                        throw new IllegalStateException("Recursive update");
                }
            }
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}
```

其中分为几种状态:

1.table为null;需要执行Node[] initTable()方法初始化Node数组,并将sizeCtl赋值为当前数组大小的0.75;

2.根据hash值计算的数组下标对应位置的Node为null;执行boolean casTabAt(Node[] tab, int i,Node c, Node v)方法初始化当前Node,操作为原子化操作;

3.需要扩容;执行helpTransfer(Node[] tab, Node f),将table指向nextTable,nextTable大小为当前table的2倍;

4.查找对应Node中的保存位置;对当前下标位置对Node加锁,做二次检查,遍历链表,查找存放对位置。