jtlgb

导航

 

好久没写过技术性文章了,还是要坚持下去。掌握的知识,能写出来或者是讲给别人听才是真正的掌握了知识,如果不善于给别人讲,实际上还是没有真正掌握相关的知识,挑个简单的写吧。

面试的时候经常会被问到hashmap和hashtable的区别。心里就开始鄙视这个面试的人了,不要拿这种基础的问题来为难一个抗战都快胜利的码农,那些个条条框框谁xx记得住。可是,遇到牛逼点的单位要问我hashmap结构、算法、这一系列牵扯到移位、与、或、非、异或2进制16进制这些我也真的答不上来,汗。。。下来恶补一下吧。
这时候,可以跟他喷这些,
Hashtable为什么性能低?
HashMap高并下发会发生什么?
HashMap内部结构是什么?
ConcurrentHashmap为什么可以支持高并发?为什么比hashmap读取效率高?
 
hashtable从java1.0就引入了,早年在深圳写java的时候,项目的全局变量里清一色的hashtable,也不了解为什么,用就行了,反正都是卖几个亿的项目,也轮不到自己操心,有时候还要鄙视一下旁边坐的华为的从c转来做java的哥们,还没我知道多。呵呵,自己就是典型的只了解业务不了解原理的码农,当年要跟着大牛混就好了,无奈,身边都是码农。
随着时间的推移,自己也知道主动去了解一些知识,例如hashmap写入慢,读取快;linkedhashmap是个有序链表读取慢,写入快;treeMap可以帮你自动做好升序排列。全局变量就是拿来读着用的嘛,得!发现用hashmap比hashtable性能好,还鄙视当年做项目时候写全局变量的人,随后,自己动手开发的项目全局变量都用起了hashmap。嗯,用着还挺好,也没发现什么问题,呵呵,都是不值钱的小项目,撑死几十个人的并发量,能有什么问题。
终于有一天,boss开始吐槽,你的程序cpu占用率怎么那么高呢,我不以为然,并发量高么,证明咱们业务量大呀,你应该高兴才对。我的数据都在内存里,速度快着呢,估计是循环和job太多了吧,boss一脸懵逼,似懂非懂,反正跟你说也不懂,出于程序员的本能,我还是检查了一遍日志,不好,日志有错误!
目标直指hashmap全局变量,怎么会这样呢,怎么说也搞了这么多年了,第一反应就是,入坑了,hashmap不是线程安全的,光顾着读取快了,没料到这个全局变量随时会多线程读写,为何会出现cpu占用率高呢,有问题,找谷哥,什么,用度娘,算了吧,除了卖假药的,屁都搜不出来。来看看为何cpu会占用率高,因为HashMap以链表组形式存在,初始数组16位(为何16位,又是一堆移位算法,下一篇文章再写吧),如果长度超过75%,长度增加一倍,多线程操作的时候,恰巧两个线程插入的时候都需要扩容,形成了两个链表,这时候读取,size不一样,报错了。其实这时候报错都是好事,至少不会陷入死循环让cpu死了,有种情况,假如两个线程在读,还有个线程在写,恰巧扩容了,这时候你死都不知道咋死的,直接就是死循环,假如你是双核cpu,cpu占用率就是50%,两个线程恰巧都进入死循环了,得!中奖了。
这该咋办,换成Hashtable吧,这点小坑难不住我,想起原来以前深圳的boss系统,其实每一行核心代码都不是随便写的,其实这个问题很早sun的大爷们都发现了,大爷们认为HashMap不是bug,而是使用场景有要求,单线程读取操作,又快又省空间。
难道我写了半天就为了把hashmap换成hashtable,那也太浪费键盘了,我今天重点说的是ConcurrentHashMap,听名字就很牛气,并发的hashmap。其实早在jdk1.5,大约2010年左右,sun的大爷们就推出了ConcurrentHashMap,线程安全,读写还快。你这不是蒙我呢,线程安全和读写速度肯定是成反比的,怎么可能。看了源码才知道,这是一种以空间换时间的结构,跟分布式缓存结构有点像,创建的时候,内存直接分为了16个segment,每个segment实际上还是存储的哈希表,写入的时候,先找到对应的segment,然后锁这个segment,写完,解锁,汗!就这么简单解决了,锁segment的时候,其他segment还可以继续工作。好像听着挺简单的,其实大爷们的代码看着真的很头疼,到处都是移位、与或非,就拿计算存放位置的代码来看,如何均匀的散列,减少位置碰撞都是有讲究的,还是得感叹你大爷就是你大爷。
posted on 2019-05-13 17:47  jtlgb  阅读(527)  评论(0编辑  收藏  举报