ConcurrentHashMap的那些事儿
HashMap允许插入key和value是null的数据的,而ConcurrentHashMap是不允许key和value是null的。这个是为什么呢?ConcurrentHashMap的作者是这么说的: The main reason that nulls aren’t allowed in ConcurrentMaps (ConcurrentHashMaps, Concurre
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还可以继续工作。好像听着挺简单的,其实代码看着真的很头疼,到处都是移位、与或非,就拿计算存放位置的代码来看,如何均匀的散列,减少位置碰撞都是有讲究的,还是山外有山,人外有人。。。
更多推荐

所有评论(0)