Java中的不同类型的线程安全集 [英] Different types of thread-safe Sets in Java

查看:237
本文介绍了Java中的不同类型的线程安全集的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

似乎有很多不同的实现和方法来生成Java中的线程安全集合。
一些示例包括



1)CopyOnWriteArraySet



2)Collections.synchronizedSet(Set set)



3) ConcurrentSkipListSet



4)Collections.newSetFromMap (new ConcurrentHashMap())



5)以类似于(4)的方式生成的其他集



这些示例来自并发模式:Java 6中的并发集实现



有人请简单解释这些例子和其他的差异,优点和缺点?

解决方案

1) CopyOnWriteArraySet 是一个非常简单的实现 - 它基本上有一个数组中的元素列表,当更改列表时,它复制数组。此时运行的迭代和其他访问继续使用旧数组,从而避免读取器和写入器之间的同步(尽管写入本身需要同步)。正常的快速设置操作(特别是 contains())在这里很慢,因为将在线性时间搜索数组。



这只用于真正小的集合,这些集合经常被读取(迭代),很少改变。 (Swings监听器集合将是一个例子,但是这些不是真正的集合,并且应该只能从EDT使用。)



2) Collections.synchronizedSet 将简单地在原始集合的每个方法上包裹同步块。您不应直接访问原始集。这意味着没有两个方法可以并发执行(一个将阻塞,直到另一个完成) - 这是线程安全的,但如果多个线程真的使用集合,你不会有并发。如果使用迭代器,通常仍然需要在外部同步,以避免在迭代器调用之间修改集合时发生ConcurrentModificationExceptions。性能将类似于原始集合的性能(但具有一些同步开销,并且如果并发使用阻塞的话)。



如果你只有低并发性,使用这个,



3) ConcurrentSkipListSet 是并发的 SortedSet 实现,大多数基本操作在O(log n)。它允许并发添加/删除和读取/迭代,其中迭代可能或可能不会告诉更改,因为迭代器创建。批量操作只是多个单独调用,而不是原子 - 其他线程只能观察其中的一些。



显然,只有当你有一些总的顺序您的元素。
这看起来像是高并发情况的理想候选对于不太大的集合(因为O(log n))。



4 )对于 ConcurrentHashMap (和Set派生自它):这里最基本的选项是(平均来说,如果你有一个好的和快速的 hashCode ))在O(1)(但可能退化到O(n)),像HashMap / HashSet。有一个有限的并发写入(表被分区,并且写访问将在所需的分区上同步),而读访问完全与自身和写线程并发(但可能尚未看到当前更改的结果书面)。迭代器可能或可能不会看到更改,因为它是创建的,批量操作不是原子的。
调整大小很慢(对于HashMap / HashSet),因此尽量通过估计创建时所需的大小(并使用大约1/3的大小,因为它在3/4 full时调整大小)来避免这种情况。 p>

当你有大的集合,一个好的(和快速的)哈希函数,并且可以估计集合大小和需要并发在创建地图之前使用这个。



5)还有其他可以在这里使用的并发映射实现吗?


There seems to be a lot of different implementations and ways to generate thread-safe Sets in Java. Some examples include

1) CopyOnWriteArraySet

2) Collections.synchronizedSet(Set set)

3) ConcurrentSkipListSet

4) Collections.newSetFromMap(new ConcurrentHashMap())

5) Other Sets generated in a way similar to (4)

These examples come from Concurrency Pattern: Concurrent Set implementations in Java 6

Could someone please simply explain the differences, advantages, and disadvantage of these examples and others? I'm having trouble understanding and keeping straight everything from the Java Std Docs.

解决方案

1) The CopyOnWriteArraySet is a quite simple implementation - it basically has a list of elements in an array, and when changing the list, it copies the array. Iterations and other accesses which are running at this time continue with the old array, avoiding necessity of synchronization between readers and writers (though writing itself needs to be synchronized). The normally fast set operations (especially contains()) are quite slow here, as the arrays will be searched in linear time.

Use this only for really small sets which will be read (iterated) often and changed seldom. (Swings listener-sets would be an example, but these are not really sets, and should be only used from the EDT anyway.)

2) Collections.synchronizedSet will simply wrap a synchronized-block around each method of the original set. You should not access the original set directly. This means that no two methods of the set can be executed concurrently (one will block until the other finishes) - this is thread-safe, but you will not have concurrency if multiple threads are really using the set. If you use the iterator, you usually still need to synchronize externally to avoid ConcurrentModificationExceptions when modifying the set between iterator calls. The performance will be like the performance of the original set (but with some synchronization overhead, and blocking if used concurrently).

Use this if you only have low concurrency, and want to be sure all changes are immediately visible to the other threads.

3) ConcurrentSkipListSet is the concurrent SortedSet implementation, with most basic operations in O(log n). It allows concurrent adding/removing and reading/iteration, where iteration may or may not tell about changes since the iterator was created. The bulk operations are simply multiple single calls, and not atomically - other threads may observe only some of them.

Obviously you can use this only if you have some total order on your elements. This looks like an ideal candidate for high-concurrency situations, for not-too-large sets (because of the O(log n)).

4) For the ConcurrentHashMap (and the Set derived from it): Here most basic options are (on average, if you have a good and fast hashCode()) in O(1) (but might degenerate to O(n)), like for HashMap/HashSet. There is a limited concurrency for writing (the table is partitioned, and write access will be synchronized on the needed partition), while read access is fully concurrent to itself and the writing threads (but might not yet see the results of the changes currently being written). The iterator may or may not see changes since it was created, and bulk operations are not atomic. Resizing is slow (as for HashMap/HashSet), thus try to avoid this by estimating the needed size on creation (and using about 1/3 more of that, as it resizes when 3/4 full).

Use this when you have large sets, a good (and fast) hash function and can estimate the set size and needed concurrency before creating the map.

5) Are there other concurrent map implementations one could use here?

这篇关于Java中的不同类型的线程安全集的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆