对于HashMap来说肯定是不行的,因为HashMap线程是不安全的。
? HashTable是直接在方法上使用synchronized,就相当于对this加锁。(此时,尝试修改俩个不同链表上的元素,都会发生冲突)可以仔细思考一下如果在不同链表上的元素,不涉及到线程的安全问题,修改不同的量。此时针对不同的链表,就不必加锁了。(不要产生锁冲突)
? ?而且HashTable的扩容是一把梭哈,就会使它不稳定时快时慢,很影响用户的体验,如果元素过多,效率则非常低,其它线程阻塞等待的时间也会更长。还有官方也不推荐使用HashTable了。
? 其实在java 1.7及以前,ConcurrentHashMap是通过“分段锁”来实现的。给若干个链表分配一把锁,这种设定,不太合适实现复杂,效率也不够,引入额外的空间开销。java8后就该为每个链表一把锁了(缩小了锁的粒度)。
?
?1.读操作不加锁
ConcurrentHashMap只对写操作进行加锁,读操作没有加锁,此时会有三种情况:
(1) 两个线程同时修改一个哈希桶时才会产生锁冲突;
(2) 两个线程同时读数据,不会有锁冲突;
(3) 一个线程修改,一个线程读,也没有锁冲突。
第三种情况可能会有线程不安全问题,这和我们写的代码有关,但是ConcurrentHashMap中的读操作使用了Volatile,来保证读到的数据不是修改了一半的数据。
2.利用了CAS的特性
? 因为synchronized里头一开始是偏向锁或轻量级锁,但是可能升级为重量级锁,这个过程不可控,充分的使用CAS就可以减少一些加锁。例如(针对哈希表元素个数的维护)。
3.针对扩容的优化
ConcurrentHashMap则是每次操作都只搬运一部分元素(化整为零,蚂蚁搬家)。它在扩容过程中,同时存在俩份哈希表,一份旧的,一份新的。插入操作,直接往新的插入。删除操作,新的旧的都删除。查找操作,新的和旧的都得查询一下。
1、HashMap线程不安全,适用于单线程环境,key值可以为null;
2、HashTable线程安全,但锁的是整个Hashtable对象,效率较低,key值不可以为null;
3、ConcurrentHashMap线程安全,锁的是每个链表的头结点,降低了锁冲突的概率;充分利用CAS机制;优化了扩容方式;key值不可以为null,适用于多线程环境。
?