目录
2.1 锁机制不同:ConcurrentHashMap 提升并发性能
3.3 ConcurrentHashMap的putVal()方法
1 问题
我们都知道Hashmap 线程不安全,而Hashtable线程安全,但Hashtable 用的不多,更推荐ConcurrentHashMap ,为什么?
2 答案
ConcurrentHashMap 是相较于 Hashtable 更推荐的线程安全 Map 实现,原因主要在于它在性能和线程安全方面都进行了优化,特别适合高并发环境中使用。下面是几方面的比较:
2.1 锁机制不同:ConcurrentHashMap 提升并发性能
• Hashtable:采用 synchronized 关键字对整个 Map 进行同步操作,所有线程必须争夺一个锁来访问或修改 Hashtable,在高并发场景下容易产生大量锁争用,导致效率低下。
• ConcurrentHashMap:采用了 分段锁(Segmented Locking),将数据划分成多个小段,每段有自己独立的锁。这样不同线程可以同时访问不同段的数据,大幅提高并发度。JDK 8 以后,ConcurrentHashMap 进一步优化为使用 CAS(Compare-And-Swap)+ 自旋锁 的方式,提高了性能和并发性。
2.2 迭代的安全性
• Hashtable:直接使用 synchronized 锁定整个表,因此在遍历时无法保证实时更新的安全性(可能导致 ConcurrentModificationException)。
• ConcurrentHashMap:采用 弱一致性迭代(弱一致性迭代器),即允许在迭代过程中进行更新操作,而不会抛出异常。迭代器能够在遍历的同时处理并发更新,确保更高的吞吐量。
2.3更好的扩展性
• ConcurrentHashMap 在高并发环境下依然能够保持高效的读写操作,而 Hashtable 在高并发场景中,随着线程数增加,其性能会显著下降。对于大规模、多线程环境下的数据存储和读取,ConcurrentHashMap 是首选。
3 带着答案理解源码
三者的区别在于是否线程安全,线程安全情况下的效率问题。接下来针对三者的put() 方法源码帮助理解:
3.1 HashMap的putVal()方法:
HashMap是执行的put()方法 然后调用的putVal()方法,如下图所示,代码中没有任何加锁的操作,因此在高并发的场景下会出现问题,因此不使用。
3.2 HashTable的put()方法
HashTable则是put() 方法 :由代码我们可以轻易看出,HashTable是直接在put方法前加上了synchronized 关键字,这意味着每次调用 put 方法时都会锁住整个 Hashtable 对象,即对 put 操作持有该对象锁。这种设计使得 Hashtable 是线程安全的,但也会导致在高并发场景下性能下降,因为一次只能有一个线程执行 put 操作,其他线程会被阻塞等待。
3.3 ConcurrentHashMap的putVal()方法
ConcurrentHashMap是执行的put()方法 然后调用的putVal()方法,因为代码过长,此处只展示关键代码,f 为插入数据的所在节点,这里使用的则是获取该节点的对象锁,而不是对整个表加锁,并且 if (tabAt(tab, i) == f) 进行CAS操作,保证了在高并发环境中的线程安全和性能优化。因此相比于其他两者更推荐使用ConcurrentHashMap。
4 总结
1. HashMap 线程不安全。
2. HashTable 线程安全,但设计不好,对整个表加锁,导致性能下降,效率低。
3. ConcurrentHashMap 线程安全,设计也较好,只会获得执行操作节点的对象锁,对该节点加锁,同时通过CAS操作,保证性能和并发性。
ConcurrentHashMap 在并发处理上具有显著的性能优势和较低的锁争用情况,因此在 Java 开发中,ConcurrentHashMap 已逐渐取代了 Hashtable,成为更好的线程安全 Map 解决方案。
标签:ConcurrentHashMap,HashMap,并发,源码,线程,Hashtable,putVal,put From: https://blog.csdn.net/weixin_49418695/article/details/143319976