我是靠谱客的博主 懵懂小馒头,最近开发中收集的这篇文章主要介绍什么?ConcurrentHashMap居然有bug,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

是的你没有看错,JDK8版本的ConcurrentHashMap真的有bug,而且不止一个。作为最基础的集合类,我们有必要了解这些bug,让我们使用时能有意识的避开。

话不多说,我们直入主题。ConcurrentHashMap有两个bug,这两个bug都和computeIfAbsent方法有关。

  • 如果对相同的key执行多次computeIfAbsent,每次都会加锁。
  • 如果对一个key嵌套执行computeIfAbsent,而嵌套执行的key的hash值刚好相同时会导致死循环。

我们都知道computeIfAbsent方法结合了以下两步操作来达到操作的原子性

  • 判断当前key是否存在。
  • 如果存在直接返回旧值,否则执行插入函数。

为了实现原子性进而保证线程安全,computeIfAbsent是通过加锁来实现的。 但是问题来了,对于相同的key,重复执行多次computeIfAbsent方法,每次都会加锁。如果你的使用场景是多线程高并发场景,这种bug无疑是会严重影响性能的。幸运的是这个bug在JDK9修复了,如果你还在用JDK8,这里提供一个workaround的办法。

public <K,V> V computeIfAbsent(ConcurrentMap<K,V> map, K key, Function<? super K, ? extends V> mappingFunction) {
	V value = map.get(key);
	if (value == null) {
		 value = map.computeIfAbsent(key,mappingFunction);
	}
	
	return value;

}

接下来我们看看第二个bug,这个bug是个致命的问题。当你对一个key嵌套执行computeIfAbsent,同时嵌套的key刚好hashcode又是相同的话就会触发这个bug。就像下面代码。

public static void main(String[] args) {
        Map<String,Integer> map = new ConcurrentHashMap();
        map.computeIfAbsent("AaAa",value-> map.computeIfAbsent("BBBB",key->1));
    }

让我们仔细分析下computeIfAbsent执行逻辑。
在这里插入图片描述

  • 第一个computeIfAbsent是没什么问题的,问题出现在第二个computeIfAbsent上,当执行到casTabAt(…) 方法时这个是false,因为已经存在一个ReservationNode了。所以binCount=0。
  • 然后因为是ReservationNode,所以hash值是-3,自然不是在扩容,第二步的if返回的是false。
  • 第三个if方法返回的是true,但是因为hash值是-3,所以不满足条件,最后又回到了最初的for循环,死循环出现。

这个bug其实很难出现,因为我们日常工作中几乎不会在computeIfAbsent中嵌套执行的情况,这种写法本身就很怪,所以还在使用JDK8的同学建议严禁使用computeIfAbsent嵌套。当然还是强烈建议你升级到JDK9。

最后

以上就是懵懂小馒头为你收集整理的什么?ConcurrentHashMap居然有bug的全部内容,希望文章能够帮你解决什么?ConcurrentHashMap居然有bug所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部