ConcurrentHashMap NullPointerException not allow null key and value
本文主要由ConcurrentHashMap的一个NullPointerException异常引起,结合ConcurrentHashMap源码分析为什么ConcurrentHashMap不允许空key和value以及如何改造进行支持。
1、异常分析
今天碰到一个异常,信息如下:
Java
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
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
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的分离锁相当熟悉
相关文章
- dracut 查看linux分区,Centos进入dracut模式,报 /dev/centos/swap does not exist,如何恢复[通俗易懂]
- Go语言中常见100问题-#20 Not understanding slice length and capacity
- 调式源码解决 seata 报错 can not get cluster name 问题
- 【错误记录】Kotlin 编译报错 ( Class ‘Xxx‘ is not abstract and does not implement abstract member )
- ORA-00335: online log string: No log with this number, log does not exist ORACLE 报错 故障修复 远程处理
- ORA-01126: database must be mounted in this instance and not open in any instance ORACLE 报错 故障修复 远程处理
- ORA-01617: cannot mount: string is not a valid thread number ORACLE 报错 故障修复 远程处理
- ORA-22344: can not specify CONVERT TO SUBSTITUTABLE option for ALTER TYPE other than NOT FINAL change ORACLE 报错 故障修复 远程处理
- ORA-23365: site string does not exist ORACLE 报错 故障修复 远程处理
- ORA-23487: object groups “string”.”string” and “string”.”string” do not have the same connection qualifier ORACLE 报错 故障修复 远程处理
- ORA-26010: Column string in table string is NOT NULL and is not being loaded ORACLE 报错 故障修复 远程处理
- ORA-26653: Apply string did not start properly and is currently in state string ORACLE 报错 故障修复 远程处理
- ORA-27432: step string does not exist for chain string.string ORACLE 报错 故障修复 远程处理
- ORA-30946: XML Schema Evolution warning: temporary tables not cleaned up ORACLE 报错 故障修复 远程处理
- ORA-32425: asynchronous and scheduled purge option is not allowed on commit SCN-based materialized view log ORACLE 报错 故障修复 远程处理
- ORA-38757: Database must be mounted and not open to FLASHBACK. ORACLE 报错 故障修复 远程处理
- ORA-38876: redo logs not required ORACLE 报错 故障修复 远程处理
- ORA-39139: Data Pump does not support XMLType objects in version string. string will be skipped. ORACLE 报错 故障修复 远程处理
- ORA-39805: Parallel loads are not allowed when loading child and parent. ORACLE 报错 故障修复 远程处理
- ORA-54008: expression column is not supported for an index organized table ORACLE 报错 故障修复 远程处理
- ORA-56605: DRCP: Session switching and migration not allowed ORACLE 报错 故障修复 远程处理
- ORA-01478: array bind may not include any LONG columns ORACLE 报错 故障修复 远程处理
- ORA-02464: Cluster definition can not be both HASH and INDEX ORACLE 报错 故障修复 远程处理
- ORA-13769: Snapshots string and string do not exist. ORACLE 报错 故障修复 远程处理
- ORA-16821: logical standby database dictionary not yet loaded ORACLE 报错 故障修复 远程处理
- MongoDb的"not master and slaveok=false"错误及解决方法
- MySQL Error number: MY-013690; Symbol: ER_SLAVE_ANONYMOUS_TO_GTID_IS_LOCAL_OR_UUID_AND_GTID_MODE_NOT_ON; SQLSTATE: HY000 报错 故障修复 远程处理
- 服务器部署,实现高可用性Note As an AI language model I can only provide a heading according to the keywords given to me It may or may not make sense Kindly proofread the language tone and content once generated by AI