查看原文
其他

【117期】推荐 2021 下半年常见 15 道 ConcurrentHashMap 面试题!

Java精选 2022-08-09

点击上方“Java精选”,选择“设为星标”

别问别人为什么,多问自己凭什么!

下方留言必回,有问必答!

每天 08:00 更新文章,每天进步一点点...

1 ConcurrentHashMap默认初始容量是多少?

从下面ConcurrentHashMap类的静态变量可以看出它的初始容量为16

2 ConCurrentHashmap 的key,value是否可以为null。

不行 如果key或者value为null会抛出空指针异常

3 ConCurrentHashmap 每次扩容是原来容量的几倍

2倍 在transfer方法里面会创建一个原数组的俩倍的node数组来存放原数据。

4 ConCurrentHashmap的数据结构是怎么样的?(后面会具体分析它的put方法)

在java1.8中,它是一个数组+链表+红黑树的数据结构。

5 存储在ConCurrentHashmap中每个节点是什么样的,有哪些变量

它是实现Map.Entry<K,V>接口。里面存放了hash,key,value,以及next节点。它的value和next节点是用volatile进行修饰,可以保证多线程之间的可见性。

6 ConCurrentHashmap的put过程是怎样的?

整体流程跟HashMap比较类似,大致是以下几步:

  • 如果桶数组未初始化,则初始化;
  • 如果待插入的元素所在的桶为空,则尝试把此元素直接插入到桶的第一个位置;
  • 如果正在扩容,则当前线程一起加入到扩容的过程中;
  • 如果待插入的元素所在的桶不为空且不在迁移元素,则锁住这个桶(分段锁);
  • 如果当前桶中元素以链表方式存储,则在链表中寻找该元素或者插入元素;
  • 如果当前桶中元素以红黑树方式存储,则在红黑树中寻找该元素或者插入元素;
  • 如果元素存在,则返回旧值;
  • 如果元素不存在,整个Map的元素个数加1,并检查是否需要扩容;

添加元素操作中使用的锁主要有(自旋锁 + CAS + synchronized + 分段锁)。关于更多集合面试题,Java精选公众号,回复Java面试,获取最新最全面试题集,支持在线随时随地刷题。

7 java1.8中ConCurrentHashmap节点是尾插还是头插?

尾插法,见上述put方法。

8 java1.8中,ConCurrentHashmap什么情况下链表才会转换成红黑树进行存储?

链表长度大于8。数组长度大于64。从put源码和以下源码可以看出:并非一开始就创建红黑树结构,如果当前Node数组长度小于阈值MIN_TREEIFY_CAPACITY,默认为64,先通过扩大数组容量为原来的两倍以缓解单个链表元素过大的性能问题。

9 java1.8中,ConCurrentHashmap的get过程是怎样的?
  • 计算 hash 值
  • 根据 hash 值找到数组对应位置: (n - 1) & h
  • 根据该位置处结点性质进行相应查找

如果该位置为 null,那么直接返回 null 就可以了

如果该位置处的节点刚好就是我们需要的,返回该节点的值即可

如果该位置节点的 hash 值小于 0,说明正在扩容,或者是红黑树,后面我们再介绍 find 方法如果以上 3 条都不满足,那就是链表,进行遍历比对即可

10 java1.8中,ConCurrentHashmap是如何计算它的size大小的?

对于size的计算,在扩容和addCount()方法就已经有处理了,可以注意一下Put函数,里面就有addCount()函数。

11 ConcurrentHashMap有哪些构造函数?

一共有五个,作用及代码如下:

//无参构造函数
    public ConcurrentHashMap() {
    }
    //可传初始容器大小的构造函数
    public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException();
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
        this.sizeCtl = cap;
    }
    //可传入map的构造函数
    public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
        this.sizeCtl = DEFAULT_CAPACITY;
        putAll(m);
    }
    //可设置阈值和初始容量
    public ConcurrentHashMap(int initialCapacity, float loadFactor) {
        this(initialCapacity, loadFactor, 1);
    }

    //可设置初始容量和阈值和并发级别,公众号Java精选,回复Java面试
    public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) 
{
        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
            initialCapacity = concurrencyLevel;   // as estimated threads
        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int)size);
        this.sizeCtl = cap;
    }
12 ConcurrentHashMap使用什么技术来保证线程安全?

jdk1.7:Segment+HashEntry来进行实现的;

jdk1.8:放弃了Segment臃肿的设计,采用Node+CAS+Synchronized来保证线程安全;

13 ConcurrentHashMap的get方法是否要加锁,为什么?

不需要,get方法采用了unsafe方法,来保证线程安全。

14 ConcurrentHashMap迭代器是强一致性还是弱一致性?HashMap呢?

弱一致性,HashMap强一直性。

ConcurrentHashMap可以支持在迭代过程中,向map添加新元素,而HashMap则抛出了ConcurrentModificationException,因为HashMap包含一个修改计数器,当你调用他的next()方法来获取下一个元素时,迭代器将会用到这个计数器。

15 ConcurrentHashMap1.7和1.8的区别

jdk1.8的实现降低锁的粒度,jdk1.7锁的粒度是基于Segment的,包含多个HashEntry,而jdk1.8锁的粒度就是Node

数据结构:jdk1.7 Segment+HashEntry;jdk1.8 数组+链表+红黑树+CAS+synchronized

作者:FFIDEAL

blog.csdn.net/xt199711/article/details/114339022

精品资料,超赞福利!

 - 小程序,3000+ 道面试题在线刷,最新、最全 Java 面试题!

期往精选  点击标题可跳转

【109期】面试官问:说说 MyBatis 和 Hibernate JPA,哪个性能更佳?

【110期】面试官:说说 RabbitMQ 消费端限流、TTL、死信队列?

【111期】面试官问:Spring Cloud 开发占用内存过高很卡,如何解决?

【112期】POI 导出 excel:设置字体、颜色、行高与列宽自适应、锁住与合并单元格

【113期】面试官问:双冒号“::”是什么语法?编程有这玩意?

【114期】ElasticSearch 搜索引擎常见面试题总结

【115期】面试官:你能说说 innodb 中行锁、间隙锁、next-key 锁吗?

【116期】面试官问:谈谈 Spring Cloud 与 Dubbo 有什么区别?

文章有帮助的话,在看,转发吧!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存