在Java
中,有一个名为HashMap的线程安全版ConcurrentHashMap和名为TreeMap的线程安全版ConcurrentSkipListMap,但没有{{} 1}} HashSet。
相反,通常有4种方法可以使用线程安全ConcurrentHashSet
:
Set
Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
Set<String> s = Collections.synchronizedSet(new HashSet<String>());
ConcurrentSkipListSet<E>
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
由于常用1和3,为什么CopyOnWriteArrayList
存在? CopyOnWriteArraySet
何时有用?
已添加: CopyOnWriteArraySet
基于CopyOnWriteArraySet
,CopyOnWriteArraySet
数据结构中的CopyOnWriteArrayList
操作为O(n),虽然contains
数据结构用于高性能List
操作,但任何人都可以解释这个吗?
答案 0 :(得分:14)
当您拥有一小组用于线程安全集合的元素时,它非常有用。
一个例子是一组听众。您需要确保唯一性并有效地迭代它们。
BTW CopyOnWriteArraySet在每个参考的基础上具有最低的开销。它可以是其他系列大小的1/6。如果你有很多它们,这将特别有用。
虽然Set数据结构是针对高性能包含操作的,有人可以解释一下吗?
COWAS在内存方面效率更高,而且contains
对于小型集合来说比其他选择更快。什么是&#34;高性能&#34;取决于用例。
答案 1 :(得分:4)
写时复制结构在功能上是不可变的。
Java曾经在一个非常糟糕的故事中提供了可写结构(如集合)的不可变视图。例如,如果您有一个集合成员,并且您公开返回它,则调用者可以转身并对其进行编辑,从而编辑对象的内部状态!但是你能做什么呢,在从任何公共函数返回之前复制整个东西?那将是毫无意义的缓慢。
这是Java历史上早期的故事。它们几乎完全依赖于不可变对象(字符串就是一个例子)。集合是这种模式的一个例外,因此从封装角度来看是有问题的。添加CopyOnWriteArraySet
后,unmodifiableCollection
和unmodifiableSet
尚未存在(虽然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
似乎不是更好。