• ConcurrentHashMap


    一:为什么要用ConcurrentHashMap?

    并发编程中使用HashMap可能会导致程序陷入死循环,而使用线程安全的HashTable效率又非常低,所以采用了ConcurrentHashMap。

    二:put方法操作主要流程

    1. ConcurrentHashMap 不允许key或value为NULL,会抛出异常
    2. 写数据前,会先对key的hash值进行一次加工spread()
    3. 整个写数据是一个 自旋(死循环) 的操作
      (1)判断tab是否初始化,如果table还没有被初始化,调用initTable()去尝试初始化table,然后继续自旋
      (2)当前table已经被初始化,调用寻址算法(hash & (table.length - 1))得到的桶位上的元素为NULL,尝试使用CAS操作将构造的节点写入桶位,成功退出循环,失败继续自旋
      (3)当前table已经被初始化了,但是当前桶位的节点是FWD节点,说明此时table正在发生扩容操作,此时当前线程就去参与扩容,扩容后继续自旋
      (4)说明当前桶位的形成了链表或者红黑树,发生了hash冲突,先使用synchronized加锁锁住当前桶位(锁对象就是当前桶位的头节点),然后判断如果当前桶位形成了链表,就遍历链表中的节点查找是否存在和待插入的节点的key完全一致的节点,如果有的话就进行value替换,没有就将节点插入到链表末尾。 如果当前桶位已经形成了红黑树,就在树中查找是否存在和待插入的节点的key完全一致的节点,存在就将value替换,不存在就插入到树中
      (5)synchronized 代码块执行结束后,判断链表长度是否 >= 阈值 8,是则转为红黑树;如果待插入节点跟桶位上的某一个节点冲突后进行替换了(无论是链表还是红黑树),直接将冲突节点的value返回 ,要么break 结束for循环
    4. for 循环处理结束后,内部先对table中的节点进行计数,然后判断是否达到扩容标准

    三:小结

    1. ConcurrentHashMap 1.8 put 方法主要采用 CAS (hash未冲突使用) + synchronized (hash冲突使用),来解决并发时的线程安全问题
    2. 链表转为红黑树,必要条件是数组长度不小于64,且链表长度不小于8

    四:为什么ConcurrentHashMap不允许null值?

    为了让ConcurrentHashMap的语义更加准确,是无法容忍模糊性的,map.get(key)返回null的时候,是没办法通过map.contains(key)检查来准确的检测,因为在检测过程中可能会被其他线程所修改,而导致检测结果并不可靠

  • 相关阅读:
    RT-Thread 快速上手(学习)
    MySQL数据库的事务
    [附源码]java毕业设计网上书店的设计
    [西湖论剑 2022]real_ez_node
    mediasoup编译之ios端
    腾讯的Tendis用了这么多牛逼技术,能否干掉Redis?
    【问题思考总结】已知对角矩阵怎么求原矩阵?原矩阵唯一吗?【相似对角化】
    DETR纯代码分享(十)matcher.py(models)匈牙利匹配算法
    线性插值方法
    黑马程序:springboot
  • 原文地址:https://blog.csdn.net/weixin_44582492/article/details/133976431