ConcurrentHashMap源码解读

ConcurrentHashMap 是Java并发包中提供的一个线程安全且高效的HashMap实现HashMap的缺点:多线程环境下HashMap会有线程安全问题,扩容可能会造成环形链表,使cpu空转达到100%,但是HashTable可以保证线程安全HashTable缺点:底层使用synchronized锁保证线程安全问题,但是将整个数组锁住了,最终只有一个线程能够调用get或者是put方法,如果没有获取锁的线程就会变为阻塞状态,效率非常低。多线程环境下建议使用Concurre
摘要由CSDN通过智能技术生成

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

HashMap的缺点:

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

HashTable缺点:

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

ConcurrentHashMap源码解读

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

ConcurrentHashMap1.7源码解读#

ConcurrentHashMap源码解读

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
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值