ConcurrentHashMap vs ConcurrentSkipListMap clarification



我想根据API文档澄清一些关于ConcurrentHashMap vs ConcurrentSkipListMap的事情。

根据我的理解,ConcurrentHashMap 保证了多个线程插入的线程安全性。因此,如果您的地图仅由多个线程同时填充,则没有问题。然而,该 API 继续建议它不会保证锁定以进行检索,因此您可能会在这里获得误导性结果?

相比之下,对于 ConcurrentSkipListMap,它声明:"插入、删除、更新和访问操作由多个线程安全地并发执行"。所以我假设这没有哈希映射具有的上述检索问题,但显然这通常会带来性能成本?

在实践中,是否有人发现由于这种特定行为而需要使用ConcurrentSkipListMap,或者检索可能提供过时的视图通常无关紧要?

ConcurrentHashMap

检索反映最近完成的更新的结果 手术在发病时保持。对于聚合操作,例如 放全部和清晰,并发检索可能反映插入或 仅删除部分条目。

它使用易失性语义进行get(key)。如果 Thread1 调用 put(key1, value1) 并且紧接着 Thread2 调用get(key1),Thread2 不会等待 Thread1 完成其put,它们不会彼此同步,并且 Thread2 可以获得旧的关联值。但是,如果在线程 2 尝试get(key1)之前put(key1, value1)在线程 1 中完成,则保证线程 2 会获得此更新 ( value1 )。

ConcurrentSkipListMap 被排序并提供

包含键、获取、 的预期平均日志 (n) 时间成本 放置和删除操作及其变体

ConcurrentSkipListMap不是那么快,但在需要排序线程安全映射时很有用。

然而,该 API 继续建议它不会保证锁定以进行检索,因此您可能会在这里获得误导性结果?

有趣的是,ConcurrentSkipListMap也没有,事实上CSLM是完全非阻塞的。

在 Java 7 中,出于所有意图和目的,CHM 在执行读取时都是非阻塞的。事实上,Java 8 更新的 CHM 实现具有完全非阻塞读取。

这里的重点是CHM和CSLM具有相似的读取语义,区别在于时间复杂度。

从您的问题中,您似乎得出的结论是,只有插入ConcurrentHashMap才是线程安全的。

根据我的理解,ConcurrentHashMap 保证了多个线程插入的线程安全性。因此,如果您的地图仅由多个线程同时填充,则没有问题。

你是怎么得出这个结论的?ConcurrentHashMap文档的第一行意味着所有操作都是线程安全的:

支持检索的完全并发性和可调整的更新预期并发的哈希表。

此外,这意味着get()操作可以保持比put()操作更高的并发级别。

简单地说ConcurrentHashMap没有您认为的检索问题。在大多数情况下,您应该使用ConcurrentHashMap而不是ConcurrentSkipListMap因为ConcurrentHashMap的性能通常优于ConcurrentSkipListMap。仅当需要具有可预测迭代顺序的ConcurrentMap或需要NavigableMap的功能时,才应使用CurrentSkipListMap

ConcurrentHashMapConcurrentSkipListMap在使用什么同步原语方面存在差异。 ConcurrentHashMap ,以及基于非阻塞Unsafe的 CAS 操作,使用阻塞synchronized构造(也称为内部监视器,它使用 monitorenter/monitorexit 字节码)来访问其"bins",而ConcurrentSkipListMap仅使用非阻塞同步。因此,可以说,从这个意义上说,ConcurrentSkipListMap更先进。

这可能看起来是一个相当微不足道的差异,几乎不会影响性能和其他一切(特别是在Java 7中synchronized块性能显着提高时),但是在Java 19+中引入虚拟线程可能会改变游戏规则。根据 JEP 425,如果虚拟线程执行synchronized块,则可能会固定虚拟线程:

通过修改同步块来避免频繁和长期的固定或频繁运行并保护可能较长的 I/O 的方法改用java.util.concurrent.locks.ReentrantLock的操作。

DZone的一篇文章讨论了这种替换。

另一方面,虚拟线程技术目前还不成熟,在我看来,没有必要急于用ReentrantLock、AQS或类似的东西替换应用程序中直接或间接使用synchronized块。

相关内容

  • 没有找到相关文章

最新更新