CopyOnWriteArraySet何时有用于实现线程
在Java
,存在名为ConcurrentHashMap的线程安全版本HashMap和名为ConcurrentSkipListMap的线程安全版本TreeMap,但没有用于HashSet的ConcurrentHashSet
。
相反,通常有4种方法可以使用线程安全的Set
:
Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
Set<String> s = Collections.synchronizedSet(new HashSet<String>());
ConcurrentSkipListSet<E>
CopyOnWriteArraySet<E>
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
由于1和3是常用的,为什么CopyOnWriteArraySet
存在? CopyOnWriteArraySet
何时有用?
补充: CopyOnWriteArraySet
是基于CopyOnWriteArrayList
, List
数据结构中的contains
操作是O(n),而Set
数据结构是用于高性能contains
操作的,有谁能解释这一点吗?
当您有一个用于线程安全集合的小组元素时,它非常有用。
一个例子是一组听众。 您需要确保唯一性并有效地对它们进行迭代。
BTW CopyOnWriteArraySet具有每个参考基础上最低的开销。 它可能只有其他馆藏的1/6。 如果你有很多它们,这是特别有用的。
而Set数据结构是用于高性能包含操作的,有谁能解释这一点?
COWAS在内存方面效率更高,而且contains
内容比其他内容更快。 什么是“高性能”取决于用例。
写入时复制结构在功能上是不可变的。
Java在某一时刻对于可写结构(例如集合)提供不可变视图有一个非常糟糕的故事。 例如,如果你有一个成员,并且你公开地返回它,调用者可以转过来编辑它,因此编辑你的对象的内部状态! 但你还能做什么,在从任何公共职能返回之前复制整个事情? 这将是毫无意义的缓慢。
这是Java历史上早期的故事。 他们几乎完全依赖于不可变的对象(字符串是一个例子)。 集合是这种模式的一个例外,因此从封装的角度来看是有问题的。 当添加CopyOnWriteArraySet
时, unmodifiableCollection
和unmodifiableSet
CopyOnWriteArraySet
尚不存在(虽然unmodifiableCollection
已经在很大程度上解决了这个问题,但我仍然发现它比其他语言提供的更麻烦的解决方案,特别是在使用自定义数据结构时)。 所以这可能解释了首先创建CopyOnWriteArraySet
的最大动机。 您可以返回CopyOnWriteArraySet
而不用担心别人会修改对象的内部状态,也不会浪费时间制作不必要的副本。
写时复制是几年前的一种时尚,但对于多线程编程而言,这是一个非常低效的想法,并且效率低于其他模型。 从你发布的文档中,他们通过创建线程本地快照来加快迭代,这意味着他们花费内存来弥补。 所以只要你的数据很小就可以使用它,因为内存快照不会浪费太多内存。
测试代码:
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
看起来不会更好。