在Java中,ConcurrentHashMap这个线程安全的集合中的Key或者Value是不允许 null(空)值出现,但是非线程安全的HashMap又允许Key或者Value插入null(空)值。why?
探寻源码
ConcurrentHashMap的put方法对key和value做了非空判断,如果为空,会抛出空指针异常:
那为什么ConcurrentHashMap如此设计呢?
并发歧义问题
对于ConcurrentHashMap不允许插入null值的问题,有人问过作者Doug Lea,以下是他回复的邮件内容:
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 allowingnulls in Maps (also Sets) is an open invitation for programsto contain errors that remain undetected untilthey break at just the wrong time. (Whether to allow nulls evenin non-concurrent Maps/Sets is one of the few design issues surroundingCollections that Josh Bloch and I have long disagreed about.)
It is very difficult to check for null keys and valuesin my entire application . Would it be easier to declare somewherestatic final Object NULL = new Object();and replace all use of nulls in uses of maps with NULL?
-Doug
————————————————
Doug Lea认为这样设计最主要的原因是:不容忍在并发场景下出现歧义!
在单线程环境中,不会存在一个线程操作该 HashMap 时,其他的线程将该 HashMap 修改的情况,可以通过 contains(key)来做判断是否存在这个键值对,从而做相应的处理。
而在多线程环境下,可能会存在多个线程同时修改键值对的情况,这时是无法通过contains(key)来判断键值对是否存在的,这会带来一个二义性的问题,Doug Lea说二义性是多线程中不能容忍的!
结论
ConcurrentHashMap在源码中加入不允许插入null(空)值的设计,主要目的是为了防止并发场景下的歧义问题。
也就是说,当一个线程从ConcurrentHashMap获取某个key,如果返回的结果是null的时候。
这个线程无法确认,这个null表示的是确实不存在这个key,还是说存在key,但是value为空。
这种不确定性会造成线程安全性问题,而ConcurrentHashMap本身又是一个线程安全的集合。
所以才这么设计。