ConcurrentHashMap(1)

第二篇内容: ConcurrentHashMap (2)

android-29\java\util\concurrent\ConcurrentHashMap.java

  • 不支持key 或者 value 为null
  • 它是线程安全的,但不是锁(synchronization details)安全的。
    This class is fully interoperable with {@code Hashtable} in programs that rely on its thread safety but not on its synchronization details.
  • 任何的检索操作、集合操作都只能代表某个时刻的状态,上述的操作也不会锁定整个Map。这些操作能够反映某个时刻集合的状态,用于状态监控或者统计是可以的,但用于程序流程控制就可能出现问题。
    Iterators, Spliterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration.
    Otherwise the results of these methods reflect transient states that may be adequate for monitoring or estimation purposes, but not for program control.
  • Iterators 遍历永远不会抛出 ConcurrentModificationException 异常。
  • 当向一个table的bin桶中插入第一个node节点时,采用CAS锁。插入非第一个节点时(比如链表添加节点),采用synchronized锁。synchronized 锁采用的对象是链表的第一个Node节点。
    Insertion (via put or its variants) of the first node in an empty bin is performed by just CASing it to the bin. This is by far the most common case for put operations under most key/hash distributions. Other update operations (insert, delete, and replace) require locks. We do not want to waste the space required to associate a distinct lock object with each bin, so instead use the first node of a bin list itself as a lock. Locking support for these locks relies on builtin "synchronized" monitors.
sizeCtrl
  • sizeCtrl的正负值具有不同的意义。sizeCtl用于控制table的初始化 或者 扩容动作。正数,具有threshold 门限值的作用;负数,又具有多线程状态同步(or 控制)的作用。
  • 注解第一句话:当sizeCtrl为负数时,table正在进行初始化或者扩容。-1 代表初始化,其他负值-(1 + the number of active resizing threads)代表正在扩容,同时能代表正在同步进行扩容的线程数量。
  • 注解第二句话:当sizeCtrl为正数或者0时,如果table为空,并且sizeCtrl为0,那么table采用默认容量DEFAULT_CAPACITY初始化。如果table为空,sizeCtrl是正数,那么table采用sizeCtrl的大小进行初始化。如果table 不为空,那么table会按照sizeCtrl的大小进行扩容。
  • 在首次初始化之后,sizeCtrl会被赋值为table大小的0.75倍。此时相当于HashMap中的threshold,当Map中元素的数量超过sizeCtrl时,就需要进行扩容动作。(见initTable函数)
  • sizeCtl会在ConcurrentHashMap构造函数里进行赋值。要么是默认值,要么是根据入参initialCapacity计算出来的值。
/**
 * Table initialization and resizing control.  When negative, the
 * table is being initialized or resized: -1 for initialization,
 * else -(1 + the number of active resizing threads).  Otherwise,
 * when table is null, holds the initial table size to use upon
 * creation, or 0 for default. After initialization, holds the
 * next element count value upon which to resize the table.
 */
private transient volatile int sizeCtl;
特殊定义的负数hash值
  • MOVED Node结点的hash值为 -1,表示ForwardingNode 类型的节点。表示当前的bin正在进行扩容迁移操作。
  • ForwardingNode 当进行迁移操作时,在bin的头结点位置插入一个ForwardingNode类型的头结点。A node inserted at head of bins during transfer operations.
/*
 * Encodings for Node hash fields.
 * Upon transfer, the old table bin contains
 * only a special forwarding node (with hash field "MOVED") that
 * contains the next table as its key. On encountering a
 * forwarding node, access and update operations restart, using
 * the new table.
 */
static final int MOVED     = -1; // hash for forwarding nodes
static final int TREEBIN   = -2; // hash for roots of trees
static final int RESERVED  = -3; // hash for transient reservations
spread 函数,用于计算hash值。
  • 扰动函数,提高散列性
  • HASH_BITS 保证二进制的最高位始终为0。通过&操作,保证Node结点的hash值永远不小于0。

static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash

static final int spread(int h) {
    return (h ^ (h >>> 16)) & HASH_BITS;
}
initTable 初始化
  • 根据sizeCtrl大小进行table的初始化,同时将sizeCtrl赋值为-1,进行多线程控制。
/**
 * Initializes table, using the size recorded in sizeCtl.
 */
private final Node<K,V>[] initTable() {
    Node<K,V>[] tab; int sc;
    while ((tab = table) == null || tab.length == 0) {
        if ((sc = sizeCtl) < 0) // sc获取sizeCtrl的值,如果小于0,说明其他线程正在进行初始化
            Thread.yield(); // lost initialization race; just spin 当前线程放弃时间片,线程进入就绪状态
         // 要开始进行初始化操作,对sizeCtrl变量赋值 -1,注意,此处不是对sc赋值
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            try {
                if ((tab = table) == null || tab.length == 0) {
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY; //
                    @SuppressWarnings("unchecked")
                    Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                    table = tab = nt;
                     // n>>>2 左移一次相当于减半,两次相当于乘以四分之一
                    sc = n - (n >>> 2); // sc赋值为n的 0.75倍
                }
            } finally {
                sizeCtl = sc; // sizeCtrl赋值正数,为初始容量的0.75倍
            }
            break;
        }
    }
    return tab;
}
putVal函数
  • putVal 是 synchronized 和 CAS 锁结合的同步方式
  • 如果table为空,先初始化table数组。initTable
  • 如果插入元素的hash&(n-1) 对应table的bin桶为null,直接使用CAS无锁赋值
static final int MOVED     = -1; // hash for forwarding nodes
static final int TREEBIN   = -2; // hash for roots of trees
static final int RESERVED  = -3; // hash for transient reservations

/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode()); // hash值永远是正的,最高位永远为0,因为 HASH_BITS
    int binCount = 0; // 表示table[hash & (n -1)] 位置bin内的元素数量(链表或者红黑树长度)。
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable(); // 初始化table
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // bin通为空
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null))) // CAS 无锁赋值
                // no lock when adding to empty bin
                break;
        } else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (f) { // synchronized锁定头结点,遍历列表进行插入
                if (tabAt(tab, i) == f) { // 校验一次头结点是否发生变化
                    if (fh >= 0) {
                        binCount = 1;
                         // 如果添加新元素到链表尾部,binCount代表链表长度
                        for (Node<K,V> e = f;; ++binCount) {
                            K ek;
                            // 相同key值的元素,直接replace value值
                            if (e.hash == hash &&
                                ((ek = e.key) == key || (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            if ((e = e.next) == null) { // 遍历到链表结尾,赋值
                                pred.next = new Node<K,V>(hash, key, value, null);
                                break;
                            }
                        }
                    } else if (f instanceof TreeBin) { // 红黑树结点,看不懂,放弃治疗了
                        Node<K,V> p;
                        binCount = 2;
                        // 树更新值
                        if ((p = ((TreeBin<K,V>)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); // Map size 增加1. 判断是否需要扩容
    return null;
}
get 函数
  • 如果在扩容过程中,进行get函数操作,每个bin桶扩容完成后,旧tab的节点位置都会被设置为ForwardingNode(hash值为负)。ForwardingNode 重写了Node节点的find方法,会在NextTable中寻找正确的hash对象。
/**
 * Returns the value to which the specified key is mapped,
 * or {@code null} if this map contains no mapping for the key.
 *
 * <p>More formally, if this map contains a mapping from a key
 * {@code k} to a value {@code v} such that {@code key.equals(k)},
 * then this method returns {@code v}; otherwise it returns
 * {@code null}.  (There can be at most one such mapping.)
 *
 * @throws NullPointerException if the specified key is null
 */
public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
    int h = spread(key.hashCode()); // hash值。可能抛出空指针异常
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (e = tabAt(tab, (n - 1) & h)) != null) {
        if ((eh = e.hash) == h) {
            if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                return e.val;
        } else if (eh < 0) // hash值为负,说明不是正常的元素节点,而是ForwardingNode节点或者TreeBin节点
            return (p = e.find(h, key)) != null ? p.val : null; // ForwardingNode类已经重写了find方法
        while ((e = e.next) != null) { // 遍历链表
            if (e.hash == h && ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}
addCount 函数
  • put新元素时,会调用addCount,增加元素数量。
  • 判断当前的count 是否超过了sizeCtrl门限值,如果超过,那么进行扩容
  • 如果sizeCtrl为负数,标示由线程正在进行扩容操作
/**
 * Base counter value, used mainly when there is no contention,
 * but also as a fallback during table initialization
 * races. Updated via CAS.
 */
private transient volatile long baseCount; // map 中元素的数量

/**
 * Table of counter cells. When non-null, size is a power of 2.
 */
private transient volatile CounterCell[] counterCells;

/**
 * Adds to count, and if table is too small and not already
 * resizing, initiates transfer. If already resizing, helps
 * perform transfer if work is available.  Rechecks occupancy
 * after a transfer to see if another resize is already needed
 * because resizings are lagging additions.
 *
 * @param x the count to add. x 代表了增加的元素个数
 * @param check if <0, don't check resize, if <= 1 only check if uncontended
 */
private final void addCount(long x, int check) {
    CounterCell[] as; long b, s; // s 代表Map的Node总数
    // 如果 counterCells 不为null 或者 baseCount 通过CAS更新失败
    if ((as = counterCells) != null ||
        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        CounterCell a; long v; int m;
        boolean uncontended = true;
        if (as == null || (m = as.length - 1) < 0 ||
            (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
            !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
            fullAddCount(x, uncontended); // 看不懂的方法
            return;
        }
        if (check <= 1)
            return;
        s = sumCount(); // 统计元素总数,统计baseCount + CounterCell
    }
    if (check >= 0) { // CAS 向table 插入新元素,check=0; 链表添加新元素 check=1; 树添加新元素,check=2
        Node<K,V>[] tab, nt; int n, sc;
        while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
               (n = tab.length) < MAXIMUM_CAPACITY) {
            int rs = resizeStamp(n); // rs是一个扩容标记戳,rs高16和低16位代表不同意义.
            if (sc < 0) { // sc 小于0,已经有线程表示正在进行扩容
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                    transferIndex <= 0) // 看不懂
                    break;
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) // 正在扩容的线程数量 +1
                    transfer(tab, nt);
            // +2 表示该线程是首个进行扩容的线程,if这个条件判断赋值了sizeCtrl
            } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2))
                transfer(tab, null); // 首次扩容操作
            s = sumCount(); // 统计元素数量, 然后while循环重新判断是否需要继续扩容
        }
    }
}

第二篇内容: ConcurrentHashMap (2)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值