今天测试在发给我一段报错日志后,根据日志定位到从ConcurrentHashMap 的缓存中get的时候,ConcurrentHashMap的底层抛出了空指针,当时感觉很奇怪为什么在get的时候产生空指针了呢?

模拟代码:

ConcurrentHashMap  concurrentHashMap = new ConcurrentHashMap();

........................................................

concurrentHashMap.get(传入的参数);

这个地方出现空指针,难道是传入的null 所以出现了空指针了,事实证明确实传入了null,但是我记得hashmap 是可以传入null 的呀? 为什么ConcurrentHashMap 却不行呢?

相应的去验证了一下,remove  方法发现也不能传入null 进去。

一、异常分析

FATAL EXCEPTION: main
java.lang.NullPointerException
at java.util.concurrent.ConcurrentHashMap.remove(ConcurrentHashMap.java:921)

本以为是并发异常,后发现不对,因为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?

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

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我们可以通过下面代码实现判断

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的分离锁相当熟悉

最后:

关于为什么这么设计:

https://laiqitech.com/125/

关于null的一些小知识:

http://www.importnew.com/14229.html

ConcurrentHashMap 产生NullPointerException的更多相关文章

  1. Java集合---ConcurrentHashMap原理分析

    集合是编程中最常用的数据结构.而谈到并发,几乎总是离不开集合这类高级数据结构的支持.比如两个线程需要同时访问一个中间临界区(Queue),比如常会用缓存作为外部文件的副本(HashMap).这篇文章主 ...

  2. ConcurrentHashMap

    ConcurrentHashMap是Java5中新增加的一个线程安全的Map集合,可以用来替代HashTable.对于ConcurrentHashMap是如何提高其效率的,可能大多人只是知道它使用了多 ...

  3. ConcurrentHashMap内存泄漏问题

    问题背景 上周,同事写了一段ConcurrentHashMap的测试代码,说往map里放了32个元素就内存溢出了,我大致看了一下他的代码及运行的jvm参数,觉得很奇怪,于是就自己捣鼓了一下.首先上一段 ...

  4. 【JUC】JDK1.8源码分析之ConcurrentHashMap(一)

    一.前言 最近几天忙着做点别的东西,今天终于有时间分析源码了,看源码感觉很爽,并且发现ConcurrentHashMap在JDK1.8版本与之前的版本在并发控制上存在很大的差别,很有必要进行认真的分析 ...

  5. Java集合——ConcurrentHashMap

    集合是编程中最常用的数据结构.而谈到并发,几乎总是离不开集合这类高级数据结构的支持.比如两个线程需要同时访问一个中间临界区(Queue),比如常会用缓存作为外部文件的副本(HashMap).这篇文章主 ...

  6. 深入剖析ConcurrentHashMap(2)

    转载自并发编程网 – ifeve.com本文链接地址: 深入剖析ConcurrentHashMap(2) 经过之前的铺垫,现在可以进入正题了.我们关注的操作有:get,put,remove 这3个操作 ...

  7. [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.

    注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...

  8. Java 线程 — ConcurrentHashMap

    ConcurrentHashMap ConcurrentHashMap 结构 采用了分段锁的方法提高COncurrentHashMap并发,一个map里面有一个Segment数组--即多个Segmen ...

  9. Java多线程系列--“JUC集合”04之 ConcurrentHashMap

    概要 本章是JUC系列的ConcurrentHashMap篇.内容包括:ConcurrentHashMap介绍ConcurrentHashMap原理和数据结构ConcurrentHashMap函数列表 ...

随机推荐

  1. SGU 223 little kings BSOJ2772 状压DP

    1896 [SCOI2005]互不侵犯King [问题描述]在n*n(1<=n<=10)的棋盘上放k(0<=k<=n*n)个国王(可攻击相邻的8 个格子),求使它们无法互相攻击 ...

  2. koa中接收前台传递的各种数据类型的方式

    标签(空格分隔): koa 数据类型接收 主要介绍三种会用到的中间件,其实都是自己在开发的过程中踩过的坑 首先介绍koa-body [详情介绍 https://github.com/dlau/koa- ...

  3. scrapy-继承默认的user-agent 中间件

    class MyUserAgentMiddleware(UserAgentMiddleware): def __init__(self, user_agent): self.user_agent = ...

  4. [转帖]go的调度机制.

    调度器 主要基于三个基本对象上,G,M,P(定义在源码的src/runtime/runtime.h文件中) G代表一个goroutine对象,每次go调用的时候,都会创建一个G对象 M代表一个线程,每 ...

  5. CSS 绝对定位与弹性布局合作居中

    position: absolute; display:flex; justify-content:center;align-items:center;

  6. true和false

    下面这些值在JavaScript中都是falsy: false 0 (数字零) "" (空字符串) null undefined NaN (一个特殊的Number值,意为Not-a ...

  7. CF235C_Cyclical Quest

    很好的一个自动机的题目. 给原串,和若干个询问串.求原串里有多少个不同子串可以通过询问串循环移动得到. 有点类似求两个串的lcs,但是灵活一点. 首先我们把询问串长度扩大一倍,去掉最后一个字符.因为最 ...

  8. There is no getter for property named 'notice' in 'class com.game.domain.Notices'

    在插入数据时报错:There is no getter for property named 'notice' in 'class com.game.domain.Notices' 四月 11, 20 ...

  9. Linux相关——记录gdb基本操作(持续更新)

    -----------2018.9.26更新标记----------- gdb的确是个很强大的东西啊,这里记录一下gdb的基本操作吧 后续可能会补充,但暂时感觉够用了就不写多了. 首先是ubuntu终 ...

  10. Spring点滴一:Spring Ioc 容器

    Spring 容器: Spring 容器是Spring框架的核心.Spring容器将创建Bean对象实例,把它们联系在一起,配置它们,并管理它们整个生命周期从创建到销毁.Spring 容器通过依赖注入 ...