【Java EE】-多线程编程(十) HashMapHashTableConcurrentHashMap之间的区别
作者
:学Java的冬瓜
博客主页
:☀冬瓜的主页🌙
专栏
:【JavaEE】
分享
:
主要内容
:HashMap和HashTable之间的区别、HashTable和ConcurrentHashMap之间的区别、
文章目录
- 一、HashMap和HashTable的区别
- 二、HashTable和ConcurrentHashMap之间的区别
- 1、最大的优化之处:ConcurrentHashMap比HashTable大大降低了锁冲突
- 2、ConcurrentHashMap只对写加锁 并使用volatile+原子的修改提高性能
- 3、ConcurrentHashMap内部使用了大量的CAS,从而提高并发效率
- 4、ConcurrentHashMap扩容使用"慢慢搬"的策略
一、HashMap和HashTable的区别
HashMap是线程不安全的
而HashTable是线程安全的,因为HashTable所有方法都加上了synchronized,是串行(同步的)。
二、HashTable和ConcurrentHashMap之间的区别
1、最大的优化之处:ConcurrentHashMap比HashTable大大降低了锁冲突
我们知道哈希表可以使用闭散列(开放地址法)和开散列(链地址法)解决哈希冲突,但是一般都是使用链地址法,也称为哈希桶。
哈希桶是使用数组 + 链表的方式组织起来。
对于HashTable来说,它的加锁是直接加上一个大锁,把整个数组+链表的操作用一把锁加锁,这会导致性能问题。比如多个线程修改一个链表上的数据时,会有线程安全问题,所以一个线程先修改,其它线程阻塞等待,这时候是没毛病的;但是如果是多个线程修改不同链表上的节点时,因为不可能找到同一个节点,没有线程安全问题,所以这个大锁就没必要加上了,加了锁,会有锁冲突,反而会降低效率。因此ConcurrentHashMap就给我们做了优化。
对于ConcurrentHashMap来说,相对于HashTable来说,ConcurrentHashMap减小了锁的粒度。在JDK1.7之前,它是使用"区间锁"(给几个链表加一个锁)的方式降低锁冲突,从而提高并发效率。在JDK1.8及之后,就给每个链表加上锁,这样就可以完美解决使用HashTable时的线程安全问题和性能问题。
2、ConcurrentHashMap只对写加锁 并使用volatile+原子的修改提高性能
HashTable是对每个方法都加锁
ConcurrentHashMap不对读加锁,只针对写加锁。
但是这样的话,多线程读没问题;多线程写,因为加锁了,无线程安全问题;多线程读和写,就有线程安全问题,可能会出现类似"脏读"的问题。
因此ConcurrentHashMap就再加上了另外一个策略:volatile和写操作原子。
3、ConcurrentHashMap内部使用了大量的CAS,从而提高并发效率
Concurrent内部充分利用了CAS,削减锁操作,从而提高效率
4、ConcurrentHashMap扩容使用"慢慢搬"的策略
在HashMap和HashTable中,插入元素,在某一次put操作,当达到扩容的条件时,会重新申请一个空间,然后把旧数组中的每个元素一个一个重新哈希转移到新数组这里。也就是说,如果数据量很大很大时,会可能存在在某一次put时,会卡一段时间的问题。
而ConcurrentHashMap对上面的场景做了优化,它使用 “慢慢搬” 的策略,即插入元素,在某一次put操作时,满足扩容条件时,先申请一个数组空间,然后先把旧数组中的元素 “搬一些” 到新数组,在每次put的时候都 “搬一点”,如果在 “搬运元素” 期间,需要get操作取数据,就从旧数组和新数组中都找。直到最后一次put时,元素 “搬运” 完,旧的数组 + 链表的全部元素都重新哈希到新的数组 + 链表的结构当中了。