分析ConcurrentHashMap源码

ConcurrentHashMap 是Java并发包中提供的一个线程安全且高效的HashMap实现

HashMap的缺点:

多线程环境下HashMap会有线程安全问题,扩容可能会造成环形链表,使cpu空转达到100%,但是HashTable可以保证线程安全

HashTable缺点:

底层使用synchronized锁保证线程安全问题,但是将整个数组锁住了,最终只有一个线程能够调用get或者是put方法,如果没有获取锁的线程就会变为阻塞状态,效率非常低。

多线程环境下建议使用ConcurrentHashMap集合

ConcurrentHashMap1.7源码解读

ConcurrentHashMap相当于把一个大的ConcurrentHashMap集合拆分成16个HashTable类,每次存放先要计算存放在哪个HashTable里面,然后还要计算存放在HashTable里面的哪个HashEntry<K,V>里面,相当于把锁的粒度进行拆分了,把大锁拆分成小锁。

核心构造参数分析

//初始的容量private static final int DEFAULT_CAPACITY = 16;//最大容量private static final int MAXIMUM_CAPACITY = 1 << 30;//HashEntry table的扩容因子private static final float LOAD_FACTOR = 0.75f;// 默认的并发度private static final int DEFAULT_CONCURRENCY_LEVEL = 16;//最小的segment数量static final int MIN_SEGMENT_TABLE_CAPACITY = 2;//最大的segment数量static final int MAX_SEGMENTS = 1 << 16; 

初始化ConcurrentHashMap

public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {     //验证参数    if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)         throw new IllegalArgumentException();     //并发级别大于并发值的话    if (concurrencyLevel > MAX_SEGMENTS)         //最大为        concurrencyLevel = MAX_SEGMENTS;     //sshift=ssize平方的次 =4    int sshift = 0;     //ssize=segments数组的容量=16    int ssize = 1;     while (ssize < concurrencyLevel) {         ++sshift;         ssize <<= 1;     }     segmentShift = 32 - sshift;  //32-4=28 计算index    segmentMask = ssize - 1;  //16-1=15  与运算的时候均匀的存放到Segment对象中    this.segments = Segment.newArray(ssize);     //数组容量最大不能大于2的三十次幂    if (initialCapacity > MAXIMUM_CAPACITY)         initialCapacity = MAXIMUM_CAPACITY;     //HashEntry 的初始容量  16/16=1    int c = initialCapacity / ssize;     //如果1*16<16    if (c * ssize < initialCapacity)         ++c;     //HashEntry table默认大小为2    int cap = MIN_SEGMENT_TABLE_CAPACITY;     while (cap < c)         cap <<= 1;  //创建第一个Segment,并放入Segment[]数组中,作为第一个Segment        Segment<K,V> s0 =new Segment<K,V>(loadFactor, (int)(cap * loadFactor),(HashEntry<K,V>[])new HashEntry[cap]);       //初始化16大小的Segment        Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];       //利用cas把s0放入ss中  ss[0]=s0        UNSAFE.putOrderedObject(ss, SBASE, s0);         //赋值给全局segments        this.segments = ss;}

这里在构造函数初始化S0,目的就是方便其他的key落到不同的Segment中,能够知道创建的Segment的参数,直接采用这个参数

put方法

    public V put(K key, V value) {        Segment<K,V> s;        //参数判断        if (value == null)            throw new NullPointerException();        //这里对key求hash值,并确定应该放到segment数组的索引位置        int hash = hash(key);        //j为索引位置 segmentShift=28  segmentMask=15  hash >>> segmentShift保留最高位4位        int j = (hash >>> segmentShift) & segmentMask;        //判断索引下是否有Segment对象,没有帮忙创建 s=Segment[j]=null        if ((s = (Segment<K,V>)UNSAFE.getObject(segments, (j << SSHIFT) + SBASE)) == null)             //创建一个Segment对象            s = ensureSegment(j);        //这里很关键,找到了对应的Segment,则把元素放到Segment中去        return s.put(key, hash, value, false);    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值