ConcurrentHashMap学习和整理

ConcurrentHashMap学习

1.  构造函数

Map的构造函数有四种,无参,单个参数,两个参数,和参数为map,以及三个参数;

三个默认参数值分别为:默认初始容量(可能就是map初始化时的数组的大小,值为16) DEFAULT_INITIAL_CAPACITY , 默认负载因子(应该是数组扩容的倍数,值为0.75) DEFAULT_LOAD_FACTOR,默认并发级别(值为16) DEFAULT_CONCURRENCY_LEVEL

  1. l  无参构造最终传三个默认参数;
  2. l  单个参数构造函数自己传入初始化时容器的大小;
  3. l  双参数构造函数需传入容器大小和扩容因子;
  4. l  参数为Map,则将map的数据的数据/默认扩容因子+1算为初始化容器大小,然后调用putAll,遍历将map的数据put到现有的数组中;
  5. l  传三个参数的构造函数是ConcurrentHashMap最终构造函数,map的构造最后都是通过调用这个方法来创建;
  •    最大并发数量为:MAX_SEGMENTS----1<<16----2^16;
  •   用并发数来创建Segment对象数组,同时并发数会保证是2^n且大于等于传入的并发数;
  •  非静态成员常量记录segmentShift=32-n;segmentMask=2^n-1;
  •  初始化容量小于等于MAXIMUM_CAPACITY--1 << 30
  •  容量/最大线程数=c,要保证c*线程数大于容量,不足就加上1,而后再保证c的值是1<<y,即,只能2^y必须>=c,创建Segment对象,闯入2^y和扩容因子,放入Segment对象数组中;
  •  Segment()对象记录非静态成员常量loadFactor记录扩容因子, (transient修饰的)(即不进行序列化)成员变量threshold记录HashEntry对象数组长度*loadFactor的值,而transientvolatile HashEntry<K,V>[] table,则保存HashEntry对象数组,数组长度为y
  •  HashEntry ,保存key hash value next四个变量,其中三个变量都是final修饰,而volatile 修饰value

2.  put方法

1.      进行判断,value的值不能为null, 否则NullPointerException

2.      会算出个hash值,然后>>>segmentShift & segmentMask来算出要插入的对应Segment数组索引,然后调用Segment.put方法.

3.      此put方法时通过ReentrantLock来实现线程同步的

4.      查看Segment的hashEntry有没有扩容因子的限定,有则进行扩容

5.      Hash & hashEntry数组长度获得下标,然后遍历下标下的链式数据,有key相同的则替代,没有则链起来

6.      Unlock解锁;

3.  putIfAbsent方法

方法内容基本和put方法一直,主要区别在于Segment.put方法最后一个参数,此参数用来表示如果出现同名的key,则不进行替代,放弃要put的新数据;

4.  get方法

特别的地方在于:如果key相同时存在,但值为null,则上锁后再次获取值,然后return;最终获取不到返回null;

5.  replace方法

V replace(key ,newvalue)和Boolean replace(key,oldervalue,newvalue),value的值不能为空,两者区别在于多了个旧值的判断和返回值类型不同,如果旧值不同也不替换…加了lock锁

6.    remove方法

加了锁,然后可以传入value,也可以不传;

7.  keys,values, elements, keySet

有意思的写法:

  final void advance() {
            if (nextEntry != null && (nextEntry = nextEntry.next) != null)
                return;

            while (nextTableIndex >= 0) {
                if ( (nextEntry = currentTable[nextTableIndex--]) != null)
                    return;
            }

            while (nextSegmentIndex >= 0) {
                Segment<K,V> seg = segments[nextSegmentIndex--];
                if (seg.count != 0) {
                    currentTable = seg.table;
                    for (int j = currentTable.length - 1; j >= 0; --j) {
                        if ( (nextEntry = currentTable[j]) != null) {
                            nextTableIndex = j - 1;
                            return;
                        }
                    }
                }
            }
        }

倒叙获取Segment,再倒序获取从hashEntry,再next一个个hashEntry

8.size()

会根据遍历累加count和操作的次数,然后相比较,这步是不加锁的,如果相等,则结束,如果不相等也就是除了增还有过其他操作,则lock之后从新累加count,最后释放锁….

 

1:扩容因子的作用:当前HashEntry对象数组已使用的容量 > 扩容因子*对象数组的长度时,则扩容,扩容的大小为原来的长度<<1,hashmap不同在于它的扩容体现在单个线程对象下的HashEntry数组;

9.hashtable 和 concurrenthashmap 的区别:

1.      hashtable使用synchronized修饰所有的方法,即以当前hashtable对象为锁; concurrenthashmap则使用的的是ReentrantLock为锁.

2.      Hashtable 的方法被调用时,其他所有方法都被上锁; concurrenthashmap只有关键的有可能造成数据出错的部分代码块会上锁,且有多处先不上锁来获取数据并判断是否会有差错,然后在上锁来获取

3.      Hashtable 是对象数组,再加上对象的next属性来形成链式结构,最终形成双列结构,concurrenthashmap则是先是Segment数组,而每个Segment对象又会保存hash Entry数组,每个hashEntry对象的next也会生成队列结构,这样更加细化后就能够保证在减小的链式结构的长度,再查询时更方便;

4.      Hashtable 的扩容保存的所有数据即将大于容量时,就翻倍扩容; concurrenthashmap则是每个SegmenthashEntry数组超出该数组的0.75就这个hashEntry数组单独扩容,这虽然造成个单个扩容需重新赋值的数据量小了,但也造成次数的增多,每次扩容的大小也小于Hashtable;

10.concurrenthashmap数据结构设计的优势

根据上述介绍的, concurrenthashmap将在hashmap的双列结构上,进行了扩充和细化,根据key的hash值进行了数据的分隔,形成Segment数组,每个Segment元素有了单独的锁,而每个Segment元素但以双列结构的形式来保存数据.这样的就造成了数据更加细化后,每个锁负责一部分数据,这些分隔开的数据在进行CRUD时就不会因上锁导致所有数据被锁定无法操作.即-------等量的数据, concurrenthashmap用来进行插入数据的话,最多可以有Segment[].length个进行同时操作,而相互不锁定,不影响,在多线程比较严重的情况下增加了效率,同时,如果是单线程的话concurrenthashmap浪费的资源就比较多,在插入数据时效率相对低一些,扩容频率要高一些.


学习过程中的一些整理,还有很多不足和不对的地方,希望各位朋友们能指正出来,谢谢....

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值