HashMap 线程安全吗?ConHashMap 怎么实现线程安全的?size 方法怎么实现线程安全的?
HashMap 线程安全吗?ConHashMap 怎么实现线程安全的?size 方法怎么实现线程安全的?
程序员朱永胜有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步, 认准
https://blog.zysicyj.top
全网最细面试题手册,支持艾宾浩斯记忆法。这是一份最全面、最详细、最高质量的 java 面试题,不建议你死记硬背,只要每天复习一遍,有个大概印象就行了。https://store.amazingmemo.com/chapterDetail/1685324709017001`
HashMap 线程安全性
HashMap 不是线程安全的。这意味着如果多个线程同时访问一个 HashMap 实例,并且至少有一个线程在结构上修改了映射(添加或删除一个或多个映射关系,或者在内部重新调整哈希表),它必须保持外部同步。这通常是通过对自然封装该映射的对象进行同步操作来完成的。
如果不存在这样的对象,映射应该使用 Collections.synchronizedMap
方法来 “ 包装 “。这最好在创建时完成,以防止意外的非同步访问:
1 | Map m = Collections.synchronizedMap(new HashMap(...)); |
ConcurrentHashMap 的线程安全实现
ConcurrentHashMap 是为了提供线程安全的哈希表而设计的。它使用了几种机制来实现线程安全:
- 分段锁(Segmentation Locks):在 ConcurrentHashMap 的早期版本中,它使用分段锁的概念,即将数据分成一段一段,每一段都有自己的锁。当一个线程需要访问一个段的数据时,它只需要获取那个段的锁,这样就可以允许多个线程并发地访问不同段的数据。
- CAS 操作(Compare-And-Swap):ConcurrentHashMap 还使用了 CAS 操作来更新其内部结构,这是一种无锁的同步机制,可以减少锁的使用,从而提高性能。
- 无锁读取:读取操作通常不需要加锁,因为它们可以安全地与更新操作并发执行。
在 Java 8 及以后的版本中,ConcurrentHashMap 的实现已经不再使用分段锁,而是通过使用节点的 synchronized 和 CAS 操作来实现线程安全,这进一步提高了并发性能。
ConcurrentHashMap 的 Size 方法
在 ConcurrentHashMap 中,size
方法的线程安全是通过一系列复杂的操作来保证的。它使用了一个循环,在循环中,它尝试估计映射的大小,如果在计算过程中映射的结构发生了变化,它可能会重试计算。
在 Java 8 中,ConcurrentHashMap 使用了一种称为 LongAdder 的结构来帮助跟踪映射的大小,这使得计算大小的操作可以更加高效地并行执行。LongAdder 使用了一组变量来分散累加的压力,而不是在单个变量上同步累加,这样可以减少线程之间的争用,从而提高性能。
在调用 size
方法时,ConcurrentHashMap 会尝试使用 LongAdder 的值来返回大小,如果在计算过程中检测到并发修改,它可能会重新计算以确保返回正确的大小。这种方法虽然不能保证绝对的精确性(因为在计算过程中可能会有新的修改发生),但它提供了一个非常接近实际大小的估计,并且这个操作的性能开销相对较低。