什么时候CopyOnWriteArraySet有用来实现线程安全的HashSet?

Java ,有一个名为ConcurrentHashMap的线程安全版HashMap和一个名为ConcurrentSkipListMap的线程安全版TreeMap ,但是HashSet没有ConcurrentHashSet

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

  1. Set mySet = Collections.newSetFromMap(new ConcurrentHashMap());
  2. Set s = Collections.synchronizedSet(new HashSet());
  3. ConcurrentSkipListSet
  4. CopyOnWriteArraySet

1使用ConcurrentHashMap keySet()来实现Set和线程安全。

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

3基于ConcurrentSkipListMap并被广泛使用。

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

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

由于常用1和3,为什么CopyOnWriteArraySet存在? CopyOnWriteArraySet什么时候有用?

补充: CopyOnWriteArraySet基于CopyOnWriteArrayList ,而List数据结构中的contains操作是O(n),而Set数据结构是针对高性能contains操作的,有人可以解释一下吗?

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

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

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

而Set数据结构是针对高性能包含操作的,有人可以解释一下吗?

COWAS在内存方面效率更高,而且对于小型集合而言,它contains的速度比替代方案更快。 什么是“高性能”取决于用例。

写时复制结构在function上是不可变的。

Java曾经在一个非常糟糕的故事中为可写结构(如集合)提供不可变的视图。 例如,如果您有一个集合成员,并且您公开返回它,则调用者可以转身并编辑它,因此可以编辑对象的内部状态! 但是你还可以做什么,在从任何公共函数返回之前复制整个东西? 那将是毫无意义的缓慢。

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

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

测试代码:

 Set a = new CopyOnWriteArraySet(); for(int i=0;i<10;i++) { a.add("str" + i); } boolean flag = true; long t1 = System.currentTimeMillis(); for(int i=0;i<200000;i++) { flag = a.contains("str" + i); } System.out.println(System.currentTimeMillis() - t1); Set b = Collections.newSetFromMap(new ConcurrentHashMap()); for(int i=0;i<10;i++) { b.add("str" + i); } t1 = System.currentTimeMillis(); for(int i=0;i<200000;i++) { flag = b.contains("str" + i); } System.out.println(System.currentTimeMillis() - t1); 

显示CopyOnWriteArraySetCollections.newSetFromMap慢。 由于测试用例是一个非常小的Set ,只读操作, CopyOnWriteArraySet似乎并不好。