数据结构
    ConcurrentHashMap的数据结构与HashMap基本类似,区别在于:
    1、内部在数据写入时加了同步机制(分段锁)保证线程安全,读操作是无锁操作;
    2、扩容时老数据的转移是并发执行的,这样扩容的效率更高。
    并发安全控制
    Java7 ConcurrentHashMap基于ReentrantLock(synchronized在1.7时效率并不高)实现分段锁,类似于mysql的行锁,只对一个桶位进行加锁,其它数组桶位不加锁,从而提高了并发能力。
    image.png
    Java8中 ConcurrentHashMap基于分段锁+CAS保证线程安全,分段锁基于synchronized关键字实现;
    image.png
    源码原理分析
    重要成员变量
    ConcurrentHashMap拥有出色的性能, 在真正掌握内部结构时, 先要掌握比较重要的成员:

    • LOAD_FACTOR: 负载因子, 默认75%, 当table使用率达到75%时, 为减少table的hash碰撞, tabel长度将扩容一倍。负载因子计算: 元素总个数%table.lengh
    • TREEIFY_THRESHOLD: 默认8, 当链表长度达到8时, 将结构转变为红黑树。
    • UNTREEIFY_THRESHOLD: 默认6, 红黑树转变为链表的阈值。
    • MIN_TRANSFER_STRIDE: 默认16, table扩容时, 每个线程最少迁移table的槽位个数。
    • MOVED: 值为-1, 当Node.hash为MOVED时, 代表着table正在扩容
    • TREEBIN, 置为-2, 代表此元素后接红黑树。
    • nextTable: table迁移过程临时变量, 在迁移过程中将元素全部迁移到nextTable上。
    • sizeCtl: 用来标志table初始化和扩容的,不同的取值代表着不同的含义:
      1. - 0: table还没有被初始化
      2. - -1: table正在初始化
      3. - 小于-1: 实际值为resizeStamp(n)<
      4. - 大于0: 初始化完成后, 代表table最大存放元素的个数, 默认为0.75*n
    • transferIndex: table容量从n扩到2n时, 是从索引n->1的元素开始迁移, transferIndex代表当前已经迁移的元素下标
    • ForwardingNode: 一个特殊的Node节点, 其hashcode=MOVED, 代表着此时table正在做扩容操作。扩容期间, 若table某个元素为null, 那么该元素设置为ForwardingNode, 当下个线程向这个元素插入数据时, 检查hashcode=MOVED, 就会帮着扩容。

    ConcurrentHashMap由三部分构成, table+链表+红黑树, 其中table是一个数组, 既然是数组, 必须要在使用时确定数组的大小, 当table存放的元素过多时, 就需要扩容, 以减少碰撞发生次数, 本文就讲解扩容的过程。扩容检查主要发生在插入元素(putVal())的过程:

    • 一个线程插完元素后, 检查table使用率, 若超过阈值, 调用transfer进行扩容
    • 一个线程插入数据时, 发现table对应元素的hash=MOVED, 那么调用helpTransfer()协助扩容。

    ConcurrentSkipListMap
    我们都知道HaspMap和ConcurrentHashMap都是无序的,如果在高并发场景下想让map有序的话可以用ConcurrentSkipListMap(跳表),跳表底层是维护索引来支持快速查找的
    image.png

    1. Map<String,Object> transactionList = new ConcurrentHashMap();
    2. transactionList.put("a","b");
    3. transactionList.remove("a");