JDK8 中 ConcurrentHashMap 变化

  1. 结构简单:JDK8 抛弃 JDK7 的 Segment 分段锁机制,由 JDK7 的两级数组变回了原来的一级数组。链表长度>=8,该链表转换为红黑树。
    在这里插入图片描述
  2. 降低锁的粒度:锁住数组的每个桶的头结点,锁粒度更小。(Hashtable 是锁住整个表、JDK7 的 ConcurrrentHashMap 是锁住一个段 Segment。而这里是锁住一个链表或者一个红黑树)
  3. 锁变化:不使用 Segment 锁(继承 ReentrantLock),利用 CAS+Synchronized来保证并发安全。
  4. 并发扩容,多个线程参与。(JDK7 的 ConncurrentHashMap 的 Segement 数组长度固定不扩容,扩容的每个 HashEntry 数组的容量,此时不需要考虑并发,因为到这里的时候,是持有该 Segment 的独占锁的)
    注意:JDK8 中,Segment 类依旧存在,但只是为了兼容,只有在序列化和反序列化时才会被用到
  5. 更多的 Node 类型
    在这里插入图片描述
  • Node<K,V>:基本结点/普通节点。当 table 中的 Entry 以链表形式存储时才使用,存储实际数据。该类的 key 和 value 不为 null(其子类可为 null)
  • TreeNode:红黑树结点。当 table 中的 Entry 以红黑树的形式存储时才会使用,存储实际数据。ConcurrentHashMap 中对 TreeNode 结点的操作都会由 TreeBin 代理执行。
  • TreeBin:代理操作 TreeNode 结点。该节点的 hash 值固定为-2,存储实际数据的红黑树的根节点。因为红黑树进行写入操作整个树的结构可能发生很大变化,会影响到读线程。因此 TreeBin 需要维护一个简单的读写锁,不用考虑写-写竞争的情况。当然并不是全部的写操作都需要加写锁,只有部分put/remove 需要加写锁
  • ForwardingNode:转发结点。该节点是一种临时结点,只有在扩容进行中才会出现,该节点的 hash 值固定为-1,并且他不存储实际数据。如果旧 table的一个 hash 桶中全部结点都迁移到新的数组中,旧 table 就在桶中放置一个ForwardingNode。当读操作或者迭代操作遇到 ForwardingNode 时,将操作转发到扩容后新的 table 数组中去执行,当写操作遇见 ForwardingNode时,则尝试帮助扩容。
    在这里插入图片描述
  • ReservationNode:保留结点,也被称为空节点。该节点的 hash 值固定为-3,不保存实际数据。正常的写操作都需要对 hash 桶的第一个节点进行加锁,如果 hash 桶的第一个节点为 null 时是无法加锁的,因此需要 new 一个ReservationNode 节点,作为 hash 桶的第一个节点,对该节点进行加锁
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值