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

时间:2015-03-25 07:23:27

标签: java multithreading collections concurrency java.util.concurrent

Java中,有一个名为HashMap的线程安全版ConcurrentHashMap和名为TreeMap的线程安全版ConcurrentSkipListMap,但没有{{} 1}} HashSet

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

  1. Set
  2. Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
  3. Set<String> s = Collections.synchronizedSet(new HashSet<String>());
  4. ConcurrentSkipListSet<E>
  5. 1使用CopyOnWriteArraySet<E>的{​​{1}}来实现keySet()和线程安全。

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

    3基于Set并且被广泛使用。

    4基于CopyOnWriteArrayList,因此它与synchronized具有相同的基本属性。以下内容从ConcurrentSkipListMap doc:http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CopyOnWriteArraySet.html

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

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

    已添加: CopyOnWriteArraySet基于CopyOnWriteArraySetCopyOnWriteArraySet数据结构中的CopyOnWriteArrayList操作为O(n),虽然contains数据结构用于高性能List操作,但任何人都可以解释这个吗?

3 个答案:

答案 0 :(得分:14)

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

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

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

  

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

COWAS在内存方面效率更高,而且contains对于小型集合来说比其他选择更快。什么是&#34;高性能&#34;取决于用例。

答案 1 :(得分:4)

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

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

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

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

答案 2 :(得分:1)

测试代码:

Set<String> a = new CopyOnWriteArraySet<String>();
    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<String> b = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
    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);

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