一.HashTable和HashMap
HashTable是JDK1.0时创建的,其在创建时考虑到了多线程情况下存在的线程安全问题,但是其解决线程安全问题的思路也相对简单:
在其众多实现方法上加上synchronized关键字(效率较低),保证其串行化执行。
但是随着业务场景的多样化,在单线程情况下不需要考虑线程安全问题,于是HashMap应运而生,同理,hashMap没有办法保证线程安全,后面随着多线程情况中的线程安全问题需求不断扩大,在多线程环境下使用哈希表有了新的解决方案。
二.JUC包下的ConcurrentHashMap
我们在这再详细说一下Hashtable存在的弊端:对整个hashtable对象加同一把锁,在多线程情况下,任意两个线程访问Hashtable中的任意数据都会产生锁竞争, size属性也是通过synchronized进行同步的,也是比较慢。同时,一旦触发扩容,该线程完成整个扩容,这个过程涉及到大量元素的拷贝,效率比较低。
ConcurrentHashMap针对这些方面进行了一系列的优化:在读时不加锁,这样大大提高了在hashmap中读取元素的效率,但是需要在元素上加volatile关键字,保证在其元素修改后能第一时间获取,只在写操作上加锁,而且它的加锁方式并不是对整个hashmap对象加锁,而是对‘桶’加锁:对hash表中的每个下标(链表头结点作为锁对象)(提高了锁密度),大大降低了锁冲突的概率。
图示就是由上面这种转化为下面这种
同时利用CAS的特性对size进行更新,效率提高
与此同时还优化了元素的更新策略:化整为零,在进行扩容时,将旧桶中的部分元素搬运到新桶中,在以后调用新桶的方法时再每次搬运部分元素,在这个过程中添加新的元素时直接往新桶中添加,在查找时在两个桶中都进行查找,直到搬运完最后一个元素,再将老桶删除掉(新桶和老桶会同时存在一段时间)
最后我们提出一个问题
concurrentHashMap和HashMap、HashTable之间的区别
①HashMap是线程不安全的
②HashTable线程安全,但是不推荐使用,因为所有的操作都加了synchronized关键字,对性能影响比较大
③ConcurrentHashMap的锁粒度小,不是对整个hash表进行加锁,而是对每一个数据的下标进行加锁
④ConcurrentHashMap对于put()加锁,对于读get()不加锁
⑤对于大量共享变量运行了volatile关键字修饰
⑥对ConcurrentHashMap对扩容进行了优化