ConcurrentHashMap NullPointerException not allow null key and value

本文主要由ConcurrentHashMap的一个NullPointerException异常引起,结合ConcurrentHashMap源码分析为什么ConcurrentHashMap不允许空key和value以及如何改造进行支持

1、异常分析
今天碰到一个异常,信息如下:

Java


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15


FATAL EXCEPTION: main

java.lang.NullPointerException

at java.util.concurrent.ConcurrentHashMap.remove(ConcurrentHashMap.java:921)

at cn.trinea.appsearch.service.AppSearchService.deleteDownloadAndUpdateViewInfo(AppSearchService.java:586)

at cn.trinea.appsearch.service.AppSearchService.installFinish(AppSearchService.java:1111)

at cn.trinea.appsearch.service.AppSearchService.access$10(AppSearchService.java:1102)

at cn.trinea.appsearch.service.AppSearchService$MyHandler.handleMessage(AppSearchService.java:1062)

at android.os.Handler.dispatchMessage(Handler.java:99)

at android.os.Looper.loop(Looper.java:153)

at android.app.ActivityThread.main(ActivityThread.java:5198)

at java.lang.reflect.Method.invokeNative(Native Method)

at java.lang.reflect.Method.invoke(Method.java:511)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:826)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:589)

at dalvik.system.NativeStart.main(Native Method)

本以为是并发异常,后发现不对,因为ConcurrentHashMap已经使用继承自ReentrantLock的Segment,保证了线程安全,Java如果有这么大的bug肯定早修复了。看了下源码


发现自己大意了,在获取hashCode时NPE(hashMap不会,因为它会对null key做一次处理)。ConcurrentHashMap不接受null key和null value这个常识一时疏忽了,调用的地方确实有可能传入null。这样在remove前进行null判断即可解决。

在问题解决后,想到hashMap和linkedhashMap都允许null key和null value,treeMap不允许null key,但允许null value,而ConcurrentHashMap既不允许null key也不允许null value,why?

2、not allow null key and value
stackoverflow并结合了源码分析了一下
(1) treeMap不允许null key是因为compare会导致NPE, 可通过以下代码实现null key支持

Java


1

2

3

4

5

6

7

8

9

10

11

12


SortedMap<Integer, Integer> map = new TreeMap<Integer, Integer>(new Comparator<Integer>() {

@Override

public int compare(Integer arg0, Integer arg1) {

if (arg0 == null) {

if (arg1 == null) return 0;

return -1;

}

if (arg1 == null) return 1;

return arg0.compareTo(arg1);

}

});

(2) ConcurrentHashMap不允许null key和null value,是因为ConcurrentHashMap的锁机制
对于get(Object key)如果返回null,ConcurrentHashMap没办法判断是key不存在还是value就是null。有人得问了那么hashmap呢, 为什么可以,hashmap我们可以通过下面代码实现判断

Java


1

2

3

4

5

6

7

8

9


Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

String key = "aa", value;

synchronized (map) {

if (map.containsKey(key)) {

value = map.get(key);

} else {

throw new Exception("key is not exist");

}

}

使用Collections.synchronizedMap对hashmap加锁,Collections.synchronizedMap的锁是同步锁,就是对象本身,所以synchronized (map)与Collections.synchronizedMap内锁统一。而ConcurrentHashMap的并发控制是利用分离锁实现的(16个重入锁),在外部无法获得锁,自己也没有提供函数进行判断,所以无奈了。

那么有没有办法既保证map的并发安全同时又允许null key和null value呢,当然了,两种方式
第一种实际上面已经展现了,通过Collections.synchronizedMap对hashmap加锁即可。synchronizedMap保证了线程安全,hashmap又允许null key和null value。

不过Collections.synchronizedMap的并发性能自然比不上ConcurrentHashMap的分桶锁机制,如果对性能要求较高
理论上也可以重写ConcurrentHashMap添加一个函数支持上面代码段的并发,但这个要求对ConcurrentHashMap的分离锁相当熟悉

时间: 2025-01-24 21:44:07

ConcurrentHashMap NullPointerException not allow null key and value的相关文章

【redis】5.spring boot项目中,直接在spring data jpa的Repository层使用redis +redis注解@Cacheable直接在Repository层使用,报错问题处理Null key returned for cache operation

spring boot整合redis:http://www.cnblogs.com/sxdcgaq8080/p/8028970.html 首先,明确一下问题的场景 之前在spring boot整合redis,关于redis的使用都是在repository层上再封装一层service层,在service层上使用的. 现在如果直接将redis的注解放在repository上使用,是个什么情况呢? 代码如下: 1.首先我有一个实体XxAdmin,主键为id 2.Xxadmin我写了一个AdminRep

Hashtable、synchronizedMap、ConcurrentHashMap 比较

java.util.concurrent包除了包含许多其他有用的并发构造块之外,还包含了一些主要集合类型List和Map的高性能的.线程安全的实现.Brian Goetz向您展示了用ConcurrentHashMap替换Hashtable或synchronizedMap,将有多少并发程序获益. 在Java类库中出现的第一个关联的集合类是Hashtable,它是JDK 1.0的一部分.Hashtable提供了一种易于使用的.线程安全的.关联的map功能,这当然也是方便的.然而,线程安全性是凭代价换

java-并发-ConcurrentHashMap高并发机制-jdk1.6

ConcurrentHashMap 是 util.concurrent 包的重要成员.本文将结合 Java 内存模型,分析 JDK 源代码,探索 ConcurrentHashMap 高并发的具体实现机制. 由于 ConcurrentHashMap 的源代码实现依赖于 Java 内存模型,所以阅读本文需要读者了解 Java 内存模型.同时,ConcurrentHashMap 的源代码会涉及到散列算法和链表数据结构,所以,读者需要对散列算法和基于链表的数据结构有所了解. 回页首 Java 内存模型

ConcurrentHashMap源码分析--Java8

        本文首写于有道云笔记,并在小组分享会分享,先整理发布,希望和大家交流探讨.云笔记地址 概述: 1.设计首要目的:维护并发可读性(get.迭代相关):次要目的:使空间消耗比HashMap相同或更好,且支持多线程高效率的初始插入(empty table). 2.HashTable线程安全,但采用synchronized,多线程下效率低下.线程1put时,线程2无法put或get. 实现原理: 锁分离:         在HashMap的基础上,将数据分段存储,ConcurrentHa

ConcurrentHashMap完全解析(jdk6/7,8)

并发编程实践中,ConcurrentHashMap是一个经常被使用的数据结构,相比于Hashtable以及Collections.synchronizedMap(),ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求(这点好像CAP理论啊 O(∩_∩)O).ConcurrentHashMap的设计与实现非常精巧,大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响,无论对于Java并发编程的学习还

深度剖析ConcurrentHashMap源码

概述 你可能会在一些技术类的书籍上看到下面这样一段关于HahsMap和Hashtable的表述: HashMap是非线程安全的,Hashtable是线程安全的. 不知道大家有什么反应,我当时只是记住了,知道面试的时候能回答上来就行了-至于为什么是线程安全的,内部怎么实现的,却不怎么了解. 今天我们将深入剖析一个比Hashtable性能更优的线程安全的Map类,它就是ConcurrentHashMap,本文基于Java 7的源码做剖析. ConcurrentHashMap的目的 多线程环境下,使用

java-并发-ConcurrentHashMap高并发机制-jdk1.8

JDK8的版本,与JDK6的版本有很大的差异.实现线程安全的思想也已经完全变了,它摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法.它沿用了与它同时期的HashMap版本的思想,底层依然由"数组"+链表+红黑树的方式思想,但是为了做到并发,又增加了很多辅助的类,例如TreeBin,Traverser等对象内部类.CAS算法实现无锁化的修改值的操作,他可以大大降低锁代理的性能消耗.这个算法的基本思想就是不断地去比较当前内存中的变量值与你指定的一个变量值是否

Java并发编程之ConcurrentHashMap

        一.什么是ConcurrentHashMap?         ConcurrentHashMap是一种线程安全且高效的HashMap.         二.为什么要引入ConcurrentHashMap?         针对Key-Value类型的集合而言,HashMap不是线程安全的,无法在多线程或高并发情况下使用,而Hashtable虽然使用synchronized关键字来保证安全,但是在高并发等线程竞争比较激烈的情况下其效率非常低下.更可怕的是,使用synchroniz

Java并发编程总结4——ConcurrentHashMap在jdk1.8中的改进(转)

一.简单回顾ConcurrentHashMap在jdk1.7中的设计 先简单看下ConcurrentHashMap类在jdk1.7中的设计,其基本结构如图所示: 每一个segment都是一个HashEntry<K,V>[] table, table中的每一个元素本质上都是一个HashEntry的单向队列.比如table[3]为首节点,table[3]->next为节点1,之后为节点2,依次类推. public class ConcurrentHashMap<K, V> ext