一、HashMap
HashMap没有对线程安全做任何有效的措施,是线程不安全的
二、HashTable
我们可以看到在HashTable的源码当中,只是简单的把关键方法加上了 synchronized 关键字,这样就相当于是直接针对HashTable对象本身进行了加锁
但这样做虽然保证了线程安全,但也存在着一些问题:
很多时候不同的线程所操作的是不同的哈希桶(链表),并不会产生线程安全问题,但HashTable仍然一棒子打死——对整体加了锁
size 属性也是通过 synchronized 来控制同步, 也是比较慢的.
一旦触发扩容, 就由该线程完成整个扩容过程. 这个过程会涉及到大量的元素拷贝, 效率会非常低.
三、ConcurrentHashMap
于是ConcurrentHashMap相比于 Hashtable 做出了一系列的改进和优化. 以 Java1.8 为例
1、ConcurrentHashMap把锁的粒度细化了,对每一个哈希桶都加了锁(lock锁)————这样只有当不同线程对同一个哈希桶中的元素进行操作时才会产生冲突,因为一个哈希表有很多哈希桶,这就大大降低了冲突的概率
2、充分利用 CAS 特性. 比如 size 属性通过 CAS 来更新. 避免出现重量级锁的情况
3、优化了扩容方式: 化整为零
发现需要扩容的线程, 只需要创建一个新的数组, 同时只搬几个元素过去.
扩容期间, 新老数组同时存在.
后续每个来操作 ConcurrentHashMap 的线程, 都会参与搬家的过程. 每个操作负责搬运一小部分元素.
搬完最后一个元素再把老数组删掉.
这个期间, 插入只往新数组加.
这个期间, 查找需要同时查新数组和老数组
四、总结
1.HashMap线程不安全、HashTable、ConcurrentHashMap线程安全
2.HashTable是整体加了一把大锁,发生锁竞争的概率较大。ConcurrentHashMap是给数组的每个下标所对应的哈希桶都加了一把锁,把锁细分了,大大降低了发生锁竞争的概率。
3.ConcurrentHashMap充分利用了CAS机制,优化了扩容方式
4.HashMap的key允许为null,其他两个不允许