1.HashTable,HashMap,ConcurrentHashMap之间的区别
(1)在线程安全上,
HashMap是线程不安全的。
HashTable和ConcurrentHashMap是线程安全的。
(2)在数据结构上,
HashMap在jdk1.7时底层结构为数组+链表;jdk1.8时底层结构为数组+链表+红黑树
ConcurrentHashMap在jdk1.7时为segment数组+链表;jdk1.8时为node数组+链表+红黑树
(3)在扩容机制上,
HashMap使用写时复制的思想,即当发现一个需要扩容的线程时,我们创建一个符合需要的新数组,然后将老数组的内容复制一份放入新数组,最后在添加完元素后,将老数组的引用指向新数组。
ConcurrentHashMap在jdk1.7时,将每个在原数组上划分的segment都可以看作是一个HashMap,每个segment内部扩容的逻辑和HashMap一样;jdk1.8优化了扩容方式,采用一种化整为零的搬家思想,在发现需要扩容的线程,只需要创建一个新的数组,同时只搬几个元素过去,扩容期间,新老数组同时存在,后续每个来操作ConcurrentHashMap的线程,都会参与搬家的过程,每个操作负责搬运一小部分元素,当搬完最后一个元素再把老数组删除掉,这个期间,插入只往新数组加,查找需要同时查找新数组和老数组。
(4)在线程安全实现上,
HashTable现在不推荐使用的一个原因是它的线程安全实现是用synchronized将锁加在普通方法上,例如,public synchronized V put(k key,V value){},锁的对象就是HashTable的实例,也就是不管HashTable是什么操作,比如只是读操作也会进行加锁,性能非常不好。
ConcurrentHshMap则是对锁进行了细化,在jdk1.7时,
一个segment数组,可以看做是一个HashMao,对数据操作也是对segment加锁,如果两个线程操作的是不同的segment,就不会冲突,如果是同一个segment,就会有锁的竞争;而jdk1.8时,
读操作没有加锁(但是使用了volatile保证从内存读取结果),只对写操作进行加锁,加锁的方式仍然时用synchronized,但不是锁整个对象,而是“锁桶”(用每个链表的头节点作为锁对象),大大降低了锁冲突概率,只要操作的不是同一个链表,就不需要排队等待,就不会互斥。并且充分利用CAS(比较并交换)的特性,比如size属性通过CAS来更新,避免出现重量级锁的情况。