博客
关于我
ConcurrentHashMap比其他并发集合的安全效率要高一些?
阅读量:377 次
发布时间:2019-03-05

本文共 1608 字,大约阅读时间需要 5 分钟。

ConcurrentHashMap的get操作无锁实现原理分析

前言

ConcurrentHashMap是Java并发集合框架中的线程安全数据结构,源自JDK1.8的改进版本。与传统的Segment加锁方式不同,它采用了基于Node的CAS(比较与交换)和synchronized的机制。在这一实现中,get操作没有加锁,这一设计背后有着深刻的内存模型和并发控制的原理。

ConcurrentHashMap的简介

在JDK1.7中,ConcurrentHashMap采用了基于Segment的实现方式,每个Segment包含多个HashEntry,并通过ReentrantLock实现锁。这种设计锁粒度较大,操作较为复杂。而JDK1.8版本则简化了数据结构,直接使用Node作为基本存储单元,采用CAS进行并发控制。这种设计使得锁粒度降低到每个HashEntry级别,操作更加高效流畅。

此外,JDK1.8版本对链表进行了优化,替换了传统的链表结构,改用红黑树来减少链表长度。当链表长度较长时,遍历效率较低,而红黑树的遍历效率显著提高。这种优化使得ConcurrentHashMap的性能得到了进一步提升。

get操作源码分析

ConcurrentHashMap的get操作的实现逻辑如下:

  • 计算hash值:首先根据键的哈希值定位到对应的表中。
  • 查找首节点:检查表中对应位置的节点,如果找到匹配的键值,则直接返回值。
  • 处理特殊情况:如果遇到扩容的ForwardingNode,调用find方法查找对应的键值。
  • 链表遍历:如果首节点不匹配,继续遍历链表,直到找到匹配的键值或遍历结束。
  • 值得注意的是,源码中没有一处加锁。这种实现依赖于Node结构中的volatile修饰和内存一致性协议来保证线程安全。

    volatile登场

    在多核处理器环境下,共享内存的可见性和一致性是一个复杂的问题。为了确保多线程环境下的读写一致性,Java引入了volatile关键字。volatile不仅保证了可见性和有序性,还能通过缓存一致性协议确保一致性。

    volatile的作用

  • 强制写入主存:对volatile变量进行修改时,JVM会立即将修改内容写入系统内存。
  • 缓存一致性:当CPU读取volatile变量时,会先检查缓存,发现缓存无效时,重新从主存中加载数据,确保所有CPU对同一共享变量的读写一致性。
  • Node的volatile修饰

    ConcurrentHashMap中的Node结构,其成员变量key、val和next都被volatile修饰。这种设计保证了在多线程环境下,线程A修改Node的值或指针时,线程B能够及时获取最新的值,避免读取到脏数据。

    此外,Node数组也被volatile修饰。这种设计主要是为了在数组扩容时,确保扩容操作对其他线程具有可见性,避免数据不一致的情况。

    是加在数组上的volatile吗?

    Node数组上的volatile并不是为了保证每个节点的值和指针都可见,而是为了确保整个数组在扩容时对其他线程可见。具体来说,数组的可见性保证了在扩容时,其他线程能够及时感知到数组的变化,避免因为数组未扩容而导致的碰撞或其他问题。

    总结

  • 无锁实现的原因:ConcurrentHashMap的get操作无锁,是因为Node结构中的val和next成员变量被volatile修饰。这种设计利用了现代处理器的缓存一致性协议,通过缓存无效机制确保读写一致性。
  • 数组的volatile修饰:数组上的volatile修饰主要是为了在扩容时保证其他线程能够及时感知到数组的变化,避免数据不一致。
  • 性能优化:相比JDK1.7的Segment加锁实现,JDK1.8的Node和红黑树的设计显著降低了锁的粒度,提高了操作效率。
  • 通过这种设计,ConcurrentHashMap在多线程环境下能够高效安全地进行数据存取操作。

    转载地址:http://govg.baihongyu.com/

    你可能感兴趣的文章
    Netty原理分析及实战(四)-客户端与服务端双向通信
    查看>>
    Netty客户端断线重连实现及问题思考
    查看>>
    Netty工作笔记0006---NIO的Buffer说明
    查看>>
    Netty工作笔记0007---NIO的三大核心组件关系
    查看>>
    Netty工作笔记0011---Channel应用案例2
    查看>>
    Netty工作笔记0013---Channel应用案例4Copy图片
    查看>>
    Netty工作笔记0014---Buffer类型化和只读
    查看>>
    Netty工作笔记0020---Selectionkey在NIO体系
    查看>>
    Vue踩坑笔记 - 关于vue静态资源引入的问题
    查看>>
    Netty工作笔记0025---SocketChannel API
    查看>>
    Netty工作笔记0027---NIO 网络编程应用--群聊系统2--服务器编写2
    查看>>
    Netty工作笔记0050---Netty核心模块1
    查看>>
    Netty工作笔记0057---Netty群聊系统服务端
    查看>>
    Netty工作笔记0060---Tcp长连接和短连接_Http长连接和短连接_UDP长连接和短连接
    查看>>
    Netty工作笔记0063---WebSocket长连接开发2
    查看>>
    Netty工作笔记0070---Protobuf使用案例Codec使用
    查看>>
    Netty工作笔记0077---handler链调用机制实例4
    查看>>
    Netty工作笔记0084---通过自定义协议解决粘包拆包问题2
    查看>>
    Netty工作笔记0085---TCP粘包拆包内容梳理
    查看>>
    Netty常用组件一
    查看>>