HashTable,HashMap和ConcurrentHashMap

发布时间:2024年01月17日

1. 在多线程环境下使用哈希表

?1.1 HashMap

对于HashMap来说肯定是不行的,因为HashMap线程是不安全的。

1.2 HashTable

? HashTable是直接在方法上使用synchronized,就相当于对this加锁。(此时,尝试修改俩个不同链表上的元素,都会发生冲突)可以仔细思考一下如果在不同链表上的元素,不涉及到线程的安全问题,修改不同的量。此时针对不同的链表,就不必加锁了。(不要产生锁冲突)

? ?而且HashTable的扩容是一把梭哈,就会使它不稳定时快时慢,很影响用户的体验,如果元素过多,效率则非常低,其它线程阻塞等待的时间也会更长。还有官方也不推荐使用HashTable了。

1.3 ConcurrentHashMap

? 其实在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,适用于多线程环境。
?

文章来源:https://blog.csdn.net/m0_74101983/article/details/135638526
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。