JDK1.7 ConcurrentHashMap源码QA解析

本文详细解析了JDK1.7 ConcurrentHashMap的源码,包括其数据结构、初始化过程、put流程以及线程安全策略。ConcurrentHashMap采用分段 Segment 的设计,每个 Segment 类似一个小型 HashMap,内置 ReentrantLock 保证并发安全。在 put 操作时,通过 CAS 和自旋锁确保线程安全。扩容过程是多线程安全的,仅限于单个 Segment 进行,避免了循环链表的问题。
摘要由CSDN通过智能技术生成

前面说到JDK1.7 HashMap是数组+链表的数据结构,但是这是线程不安全的。而ConcurrentHashMap则是HashMap的升级版解决了线程安全问题,而且底层大部分逻辑和HashMap是相似的。接下来还是通过QA方式解析源码。

1.ConcurrentHashMap的数据结构是怎么样的?初始化过程是怎么样的?

(1)首先,ConcurrentHashMap设计是作为线程安全的Map集合,那么就需要考虑怎么支持并发操作,我们用HashMap进行对比,就可以知道HashMap要想并发安全操作就不能不对整个HashMap进行加锁,这样大大影响了整个容器的性能。

作为解决这个问题的设计者们,在ConcurrentHashMap就想到了基于HashMap的数组+链表的思想,将每个数组都拆分成一个个小型各自独立的Segment(每个Segment相当于一个个小型HashMap),每个线程加锁只对各自独立的Segment去加锁操作,这样就大大提升了性能。于是就引入了“分段Segment”的思想,每个分段还集成了ReentrantLock可以对内部HashEntry数据进行CAS操作,这样就保证了线程安全。基于此,ConcurrentHashMap的数据结构图大致可以如下:

上图可知:在JDK7的ConcurrentHashMap中,首先有一个Segment数组,存的是Segment对象,Segment相当于一个小HashMap,而Segment内部有一个HashEntry的数组用于存储实际数据。

 (2)ConcurrentHashMap有哪些核心属性,数据结构是怎么初始化的?

从上面知道ConcurrentHashMap与HashMap的属性相似,不同的是ConcurrentHashMap内部有一个Segment数组,而每个Segment内部存储HashEntry数组。

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
        implements ConcurrentMap<K, V>, Serializable {


    //默认初始化容量大小16
    static final int DEFAULT_INITIAL_CAPACITY = 16;

    /**
     * 默认加载因子0.75
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    /**
     * Segment分段的数量,默认也是16个分段数
     */
    static final int DEFAULT_CONCURRENCY_LEVEL = 16;

    /**
     * 最大容量数:2^30,保证所有元素可以存储不越界
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

    /**
     * 每个Segment内部的HashMap数组最小长度:默认最小为2
     */
    static final int MIN_SEGMENT_TABLE_CAPACITY = 2;

    /**
     * 每个Segment内部的HashMap数组最大长度:最大为2^16
     */
    static final int MAX_SEGMENTS = 1 << 16;

    /**
     * 尝试非阻塞加锁次数,如果超过次数还没能加锁则转换为阻塞等待加锁
     */
    static final int RETRIES_BEFORE_LOCK = 2;

    /**
     * hash种子,可影响hash散列
     */
    private transient final int hashSeed = randomHashSeed(this);

    /**
     * 当前segments数组的最大下标
     */
    final int segmentMask;

    /**
     * 32-(segments数组长度最大下标对应二进制最高位1开始往后占据的位数)
     * 例如20=0001 0100,则最高位1开始往后共占据5个位数,则segmentShift=32-5=27。
     * 相当于保留hash值的高n位,用于put操作对hash右移segmentShift位数,使hash的高n位
     * 算出要存在segments哪个位置。
     */
    final int segmentShift;

    /**
     * 最核心的segment分段数组,是一个Segment对象
     */
    final Segment<K,V>[] segments;


    /**
     * HashEntry定义,和HashMap定义一样。
     * 不同的是内部的next属性是通过UNSAFE工具类线程安全方式获取;
     * hash属性的高位用于计算segment下标,低位又用于计算table下标
     */
    static final class HashEntry
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值