ConcurrentHashMap的那些事儿

        HashMap允许插入key和value是null的数据的,而ConcurrentHashMap是不允许key和value是null的。这个是为什么呢?ConcurrentHashMap的作者是这么说的: 
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. 

        简单来说,这与数据结构是否支持并发息息相关。当ConcurrentMaps使用map.get(key)时返回为null,无法判断key是不存在还是值为空,non-concurrent还可以再调用map.contains(key)检查,但ConcurrentMaps可能再两次调用间已经发生改变。

         我们应该知道hashmap写入慢,读取快;linkedhashmap是个有序链表读取慢,写入快;treeMap可以帮你自动做好升序排列。此时我想起来一个坑,当时认为全局变量就是拿来读着用的!发现用hashmap比hashtable性能好,随后,自己动手开发的项目全局变量都用起了hashmap。嗯,用着还挺好,也没发现什么问题。

        终于有一天,同事开始吐槽程序cpu占用率高呢,我不以为然,我的数据都在内存里,速度快着呢,但是我还是检查了一遍日志,此时发现问题了!

        为何会出现cpu占用率高呢,目标直指hashmap全局变量,hashmap不是线程安全的,读取是快了,但是随时会多线程读写,因为HashMap以链表组形式存在,初始数组16位(一堆移位算法,我也懵逼),如果长度超过75%,长度增加一倍,多线程操作的时候,恰巧两个线程插入的时候都需要扩容,形成了两个链表,这时候读取,size不一样,报错了。其实这时候报错都是好事,至少不会陷入死循环让cpu死了,有种情况,假如两个线程在读,还有个线程在写,恰巧扩容了,直接就是死循环,假如你是双核cpu,cpu占用率就是50%,两个线程恰巧都进入死循环了。SUN认为HashMap不是bug,而是使用场景有要求,单线程读取操作,又快又省空间。

        难道把hashmap换成hashtable,我今天重点说的是ConcurrentHashMap,并发的hashmap。其实早在jdk1.5,sun的就推出了ConcurrentHashMap,线程安全,这是一种以空间换时间的结构,跟分布式缓存结构有点像,创建的时候,内存直接分为了16个segment,每个segment实际上还是存储的哈希表,写入的时候,先找到对应的segment,然后锁这个segment,写完,解锁!就这么简单解决了,锁segment的时候,其他segment还可以继续工作。好像听着挺简单的,其实代码看着真的很头疼,到处都是移位、与或非,就拿计算存放位置的代码来看,如何均匀的散列,减少位置碰撞都是有讲究的,还是山外有山,人外有人。。。

你可能感兴趣的:(开发轶事,细节深究)