1.8 ConcurrentHashMap 总结

底层结构

Node 数组 + 链表 + 红黑树

原理

synchronized + CAS

构造方法
public ConcurrentHashMap(int initialCapacity) {  
    if (initialCapacity < 0) 
        throw new IllegalArgumentException();   
    int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
            MAXIMUM_CAPACITY :              
            tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));  
    this.sizeCtl = cap;
}

public ConcurrentHashMap(int initialCapacity, float loadFactor) {
    this(initialCapacity, loadFactor, 1);
}

public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
    if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)   
        throw new IllegalArgumentException();   
    if (initialCapacity < concurrencyLevel)   // Use at least as many bins     
        initialCapacity = concurrencyLevel;   // as estimated threads   
    long size = (long)(1.0 + (long)initialCapacity / loadFactor);
    int cap = (size >= (long)MAXIMUM_CAPACITY) ?    
        MAXIMUM_CAPACITY : tableSizeFor((int)size);
    this.sizeCtl = cap;
}
put 方法
  • key 和 value 不能为 null,否则抛出空指针异常
  • 计算 hash 值:key.hashCode() 高 16 位与低 16 位异或后将最高位(符号位)置 0,保证其非负,因为 hash 值为负数时有特殊含义:
    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
    
  • 判断数组是否已初始化,若未初始化则调用 initTable() 方法进行初始化,使用了 CAS 操作
  • 找到下标处的元素 (下标计算方式为 hash & length - 1)
  • 如果下标位置的元素为 null 则直接插入 (CAS)
  • 若正在扩容则帮助扩容
  • synchronized 锁住下标处的元素
    • 链表:使用尾插法插入新节点,若插入后链表长度超过 8 且数组长度 >= 64 则进行转红黑树(第二个条件不满足则进行扩容)
    • 红黑树:调用 putTreeVal 方法将节点插入到红黑树中
  • 调用 addCount 方法,计数并判断是否需要扩容 (扩容条件好像比 元素数量(插入后) > 容量 * 加载因子(默认0.75) 还要强一点,fullAddCount 后直接 return 了,暂时还没弄清楚);baceCount 和 counterCells 共同计数,获取元素个数时需要将 baseCount 和 counterCells 中各个位置的 value 加起来
扩容
  • 若其他线程已经在扩容了则帮助扩容
  • 数据迁移过程
    • synchronized 锁住下标处的元素
      • 链表:找到最后一段新下标相同的小链表,遍历其他元素,按新下标位置构造两条链表 (头插法),然后一次性移过去 (效果与 1.7 相同),最后把老数组上该位置的元素设置为 ForwardingNode 对象
      • 红黑树:按新下标构造两条双向链表,若链表长度小于等于 6 则将该链表的节点类型 (TreeNode) 转换为普通链表节点,若链表长度大于 6,如果另一条链表不为空则需要重新树化 (另一条链表为空则就是原来那棵树整个移过去)
链表转红黑树

与 HashMap 类似,不过数组元素的类型是 TreeBin 而不是 TreeNode,树的根节点作为 TreeBin 的属性,这样根节点的改变就不会对锁造成影响了,因为锁的是 TreeBin 对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值