ConcurrentHashMap原理

ConcurrentHashMap 1.7

 Java 7 中 ConcurrentHashMap 的存储结构如上图,ConcurrnetHashMap 由很多个 Segment 组合,而每一个 Segment 是一个类似于 HashMap 的结构,所以每一个 HashMap 的内部可以进行扩容。但是 Segment 的个数一旦初始化就不能改变,默认 Segment 的个数是 16 个,你也可以认为 ConcurrentHashMap 默认支持最多 16 个线程 并发。

Java 7 中 ConcurrnetHashMap 的初始化

1. 必要参数校验。

2. 校验并发级别 concurrencyLevel 大小,如果大于最大值,重置为最大值。无参构造 默认值是 16.

3. 寻找并发级别 concurrencyLevel 之上最近的 2 的幂次方值,作为初始化容量大小, 默认是 16。

4. 记录 segmentShift 偏移量,这个值为【容量 = 2 的N次方】中的 N,在后面 Put 时 计算位置时会用到。默认是 32 - sshift = 28.

5. 记录 segmentMask,默认是 ssize - 1 = 16 -1 = 15.

6. 初始化 segments[0],默认大小为 2,负载因子 0.75,扩容阀值是 2*0.75=1.5,插 入第二个值时才会进行扩容。

扩容 rehash

ConcurrentHashMap 的扩容只会扩容到原来的两倍。老数组里的数据移动到新的数组 时,位置要么不变,要么变为 index+ oldSize,参数里的 node 会在扩容之后使用链表头 插法插入到指定位置。

ConcurrentHashMap 1.8

 可以发现 Java8 的 ConcurrentHashMap 相对于 Java7 来说变化比较大,不再是之前的 Segment 数组 + HashEntry 数组 + 链表,而是 Node 数组 + 链表 / 红黑树。当冲突链 表达到一定长度时,链表会转换成红黑树。

Java 8 中 ConcurrnetHashMap 的初始化

/**
* 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) {
        // 如果 sizeCtl < 0 ,说明另外的线程执行CAS 成功,正在进行初始化。
        if ((sc = sizeCtl) < 0)
        // 让出 CPU 使用权
        Thread.yield(); // lost initialization race; just spin
        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;
            sc = n - (n >>> 2);
            }
        } finally {
            sizeCtl = sc;
        }
            break;
        }
    }
    return tab;
}

从源码中可以发现 ConcurrentHashMap 的初始化是通过自旋和 CAS 操作完成的。里面 需要注意的是变量 sizeCtl ,它的值决定着当前的初始化状态。

1. -1 说明正在初始化

2. -N 说明有N-1个线程正在进行扩容

3. 表示 table 初始化大小,如果 table 没有初始化

4. 表示 table 容量,如果 table 已经初始化。

总结

Java7 中 ConcurrentHashMap 使用的分段锁,也就是每一个 Segment 上同时只有一个 线程可以操作,每一个 Segment 都是一个类似 HashMap 数组的结构,它可以扩容,它的冲突会转化为链表。但是 Segment 的个数一但初始化就不能改变。

Java8 中的 ConcurrentHashMap 使用的 Synchronized 锁加 CAS 的机制。结构也由 Java7 中的 Segment 数组 + HashEntry 数组 + 链表 进化成了 Node 数组 + 链表 / 红黑 树,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑 树,在冲突小于一定数量时又退回链表。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值