• 关于ConcurrentHashMap的key和value不能为null的深层次原因


    https://blog.csdn.net/dhaibo1986/article/details/108285293

    关于ConcurrentHashMap的key和value不能为null的深层次原因

    冬天里的懒猫 2020-08-28 18:58:38 958 收藏 5
    分类专栏: JAVA 文章标签: java 多线程 hashmap 面试
    版权
    前面分析ConcurrentHashMap的过程中可以发现,其要求key和value不能为空。实际上,不仅仅是ConcurrentHashMap,前面的HashTable,以及ConcurrentSkipListMap,这些并发的Map都不允许为空。在面试的过程中,不少大厂也会拿这个问题做为追问的问题之一。那么我们就来具体聊聊为什么不能为null的深层次的原因。

    层次1:源码不支持
    是的,实际上确实是在源码上就没用提供支持。在hashtable中,对value进行了非空校验,而key,如果为空则会出现NullPointerException。

    if (value == null) {
    throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
     
    key如果为空,则hashCode方法会出现空指针异常。
    在ConcurrentHashMap中,和ConcurrentSkipListMap中,则分别进行了非空约束。
    ConcurrentHashMap:

    if (key == null || value == null) throw new NullPointerException();
    1
    ConcurrentSkipListMap:

    public V put(K key, V value) {
    if (value == null)
    throw new NullPointerException();
    return doPut(key, value, false);
    }
     
    而这个doPut方法中:

    if (key == null)
    throw new NullPointerException();
     
    从上面可以看出,在代码中直接就杜绝了使用null的可能性,只有HashMap是支持null的,但是是在put为空的时候,hash方法对null做了特殊处理,为null的时候hash值位0。

    层次2:null会带来二义性
    之所以并发的ConcurrentHashMap不支持null的深层次的原因在于,null会带来难以容忍的二义性。我们可以看看Doug Lea对这个问题的描述。
    Handling Null Values in ConcurrentHashMap

    Tutika Chakravarthy wrote:
    > Hi ,
    > I would like to replace some Hashmaps in our
    > application, which are prone to multi threading issues
    > with ConCurrentHashMap.
    >
    > Currently we keep null key and values in hashmap
    > without any issues as HashMap allows them.
    >
    > But ConcurrentHashMap does not allow any null key and
    > values .
    >

    Try to take Holger's advice. As mostly an aside though...

    The main reason that nulls aren't allowed in ConcurrentMaps
    (ConcurrentHashMaps, ConcurrentSkipListMaps) is that
    ambiguities that may be just barely tolerable in non-concurrent
    maps can't be accommodated. The main one is that if
    map.get(key) returns null, you can't detect whether the
    key explicitly maps to null vs the key isn't mapped.
    In a non-concurrent map, you can check this via map.contains(key),
    but in a concurrent one, the map might have changed between calls.

    Further digressing: I personally think that allowing
    nulls in Maps (also Sets) is an open invitation for programs
    to contain errors that remain undetected until
    they break at just the wrong time. (Whether to allow nulls even
    in non-concurrent Maps/Sets is one of the few design issues surrounding
    Collections that Josh Bloch and I have long disagreed about.)

    >
    > It is very difficult to check for null keys and values
    > in my entire application .
    >

    Would it be easier to declare somewhere
    static final Object NULL = new Object();
    and replace all use of nulls in uses of maps with NULL?

    -Doug
     
    原作者认为,在ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps)上,不允许null值的出现的主要原因是他可能会在并发的情况下带来难以容忍的二义性。如果在HashMap等非并发容器中,你可以通过contains方法来判断,这个key是究竟不存在,还是本来就是null。但是在并发容器中,如果允许空值的存在的话,你就没法判断真正的情况。用作者的话说就是:在Maps或者Sets集合中允许null值的存在,就是公开邀请错误进入你的程序。而这些错误,只有在发生错误的情况下才能被发现。
    我们可以对HashMap进行测试:

    public static void main(String[] args) {
    HashMap map = new HashMap();
    map.put(null,1);
    System.out.println(map.containsKey(null));
    map.put(1,null);
    System.out.println(map.containsValue(null));
    System.out.println(map.get(null));
    }
     
    此时输出:

    true
    true
     
    可见,在HashMap之中,我们可以很容易的通过contains方法来判断key或者value为null是否真的存在。
    但是这个问题要是出现在ConurrentMaps中了,那么就可能会有问题了。试想一下,当我们首先从map中get某个key,由于map中这个key不存在,那么会返回null,这之后我们通过contains进行判断,此时如果有线程并发写入了一条value为null的值,那么contains的结果就为true。这样就会与真实的情况不一致了,这就是二义性。
    因此我们也需要注意Doug 的观点:不管容器是否考虑了线程安全问题,都不应该允许null值的出现。他觉得在现有的某些集合里面允许了null值的出现,是集合的设计问题。
    ————————————————
    版权声明:本文为CSDN博主「冬天里的懒猫」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/dhaibo1986/article/details/108285293

  • 相关阅读:
    java设计模式之单例模式
    走台阶问题的递归方法与非递归方法
    QueenAttack
    为什么要建立数据仓库?
    通过复制现有的redhat虚拟机的文件,实现在VMWare8.0上重建一个新的redhat虚拟机环境
    hive配置以及在启动过程中出现的问题
    java_ee_sdk-7u2的安装与 启动
    Hadoop集群配置过程中需要注意的问题
    VMware8.0虚拟机中安装Ubuntu12.04使用NAT设置连接网络
    在VMware8.0.4安装centos6.3出现蓝屏,显示“anaconda: Fatal IO error 104 (Connection reset by peer) on X server :1.0. install exited abnormally [1/1]”?
  • 原文地址:https://www.cnblogs.com/kelelipeng/p/14738119.html
Copyright © 2020-2023  润新知