CopyOnWriteArraySet何时可用于实现线程安全的HashSet?

在 中,有名为 ConcurrentHashMap 的线程安全版本 HashMap 和名为 ConcurrentSkipListMap 的线程安全版本 TreeMap,但没有 HashSetJavaConcurrentHashSet

相反,通常有4种方法可以使用线程安全:Set

  1. Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
  2. Set<String> s = Collections.synchronizedSet(new HashSet<String>());
  3. ConcurrentSkipListSet<E>
  4. CopyOnWriteArraySet<E>

1.使用实现两者兼而有之,线程安全。keySet()ConcurrentHashMapSet

2.使用方式,似乎这种方式是不推荐的。synchronized

3.基于并得到广泛应用。ConcurrentSkipListMap

4 基于 CopyOnWriteArrayList,因此它共享 相同的基本属性。以下是从文档中选择:http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CopyOnWriteArraySet.htmlCopyOnWriteArrayListCopyOnWriteArraySet

  • 它最适合于以下应用程序:在以下应用程序中,集合大小通常保持较小,只读操作的数量远远超过突变操作,并且您需要在遍历期间防止线程之间的干扰。
  • 它是线程安全的。
  • 突变操作(添加、设置、删除等)成本高昂,因为它们通常需要复制整个底层阵列。
  • 迭代器不支持突变删除操作。
  • 通过迭代器进行遍历速度很快,并且不会遇到来自其他线程的干扰。
  • 迭代器依赖于构造迭代器时数组的不变快照。

既然1和3是常用的,为什么存在?什么时候有用?CopyOnWriteArraySetCopyOnWriteArraySet

补充:基于,数据结构中的运算是O(n),而数据结构是高性能运算,谁能解释一下呢?CopyOnWriteArraySetCopyOnWriteArrayListcontainsListSetcontains


答案 1

当您有一小组用于线程安全集合的元素时,它很有用。

一个示例是一组侦听器。您需要确保唯一性并有效地迭代它们。

BTW CopyOnWriteArraySet在每个引用的基础上具有最低的开销。它可以是其他集合大小的 1/6。如果您有很多,这特别有用。

虽然设置数据结构是用于高性能包含操作,但任何人都可以解释这一点吗?

COWAS在内存方面更有效,对于小型集合来说,它比替代方案更快。什么是“高性能”取决于用例。contains


答案 2

写入时复制结构在功能上是不可变的。

Java在提供关于可写结构(如集合)的不可变视图方面一度非常糟糕。例如,如果您有一个 set 成员,并且您公开返回了它,则调用方可以转身编辑它,从而编辑对象的内部状态!但是你还能做什么,在从任何公共功能返回之前复制整个东西呢?这将是毫无意义的缓慢。

这是Java历史上早期的故事。他们几乎完全依赖于不可变对象(字符串就是一个例子)。集合是这种模式的一个例外,因此从封装的角度来看是有问题的。何时添加,并且尚不存在(尽管在很大程度上解决了问题,但我仍然发现它是一个比其他语言提供的解决方案更麻烦的解决方案,尤其是在使用自定义数据结构时)。因此,这可能解释了最初创作的最大动机。您可以返回 a,而不必担心其他人修改您的对象的内部状态,也不会浪费时间制作不必要的副本。CopyOnWriteArraySetunmodifiableCollectionunmodifiableSetunmodifiableCollectionCopyOnWriteArraySetCopyOnWriteArraySet

几年前,Copy-On-Write是一种时尚,但对于多线程编程来说,这是一个众所周知的低效想法,并且效率低于其他模型。从您发布的文档中,他们通过创建线程本地快照来加速迭代它,这意味着他们正在花费内存来补偿。因此,只要您的数据很小,就可以使用这个完全可以的类...因为内存快照不会增加太多浪费的内存。