\(^_^)/ Java中的并发集合(Concurrent Collections)

 

 

并发集合:

 

用于多线程上下文中的 Collection 实现:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和 CopyOnWriteArraySet。

当期望许多线程访问一个给定 collection 时,ConcurrentHashMap 通常优于同步的 HashMap,ConcurrentSkipListMap 通常优于同步的 TreeMap。

当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList。 

此包中与某些类一起使用的“Concurrent&rdquo前缀;是一种简写,表明与类似的“同步”类有所不同。

例如,java.util.Hashtable 和 Collections.synchronizedMap(new HashMap()) 是同步的,但 ConcurrentHashMap 则是“并发的”。

并发 collection 是线程安全的,但是不受单个排他锁的管理。

在 ConcurrentHashMap 这一特定情况下,它可以安全地允许进行任意数目的并发读取,以及数目可调的并发写入。

需要通过单个锁不允许对 collection 的所有访问时,“同步”类是很有用的,其代价是较差的可伸缩性。

在期望多个线程访问公共 collection 的其他情况中,通常“并发”版本要更好一些。

当 collection 是未共享的,或者仅保持其他锁时 collection 是可访问的情况下,非同步 collection 则要更好一些。 

大多数并发 Collection 实现(包括大多数 Queue)与常规的 java.util 约定也不同,因为它们的迭代器提供了弱一致的,而不是快速失败的遍历。

弱一致的迭代器是线程安全的,但是在迭代时没有必要冻结 collection,所以它不一定反映自迭代器创建以来的所有更新。 

 

 

1.ConcurrentHashMap

(1).支持获取的完全并发和更新的所期望可调整并发的哈希表。

(2).此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。

(3).尽管所有操作都是线程安全的,但获取操作不 必锁定,并且不 支持以某种防止所有访问的方式锁定整个表。

(4).此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。 

(5).获取操作(包括 get)通常不会受阻塞,因此,可能与更新操作交迭(包括 put 和 remove)。

(6).获取会影响最近完成的 更新操作的结果。

(7).对于一些聚合操作,比如 putAll 和 clear,并发获取可能只影响某些条目的插入和移除。类似地,在创建迭代器/枚举时或自此之后,Iterators 和 Enumerations 返回在某一时间点上影响哈希表状态的元素。它们不会 抛出 ConcurrentModificationException。

(8).迭代器被设计成每次仅由一个线程使用。 

(9).这允许通过可选的 concurrencyLevel 构造方法参数(默认值为 16)来引导更新操作之间的并发,该参数用作内部调整大小的一个提示。表是在内部进行分区的,试图允许指示无争用并发更新的数量。因为哈希表中的位置基本上是随意的,所以实际的并发将各不相同。

(10).理想情况下,应该选择一个尽可能多地容纳并发修改该表的线程的值。使用一个比所需要的值高很多的值可能会浪费空间和时间,而使用一个显然低很多的值可能导致线程争用。对数量级估计过高或估计过低通常都会带来非常显著的影响。

(11).当仅有一个线程将执行修改操作,而其他所有线程都只是执行读取操作时,才认为某个值是合适的。

(12).重新调整此类或其他任何种类哈希表的大小都是一个相对较慢的操作,因此,在可能的时候,提供构造方法中期望表大小的估计值是一个好主意。 

(13).此类及其视图和迭代器实现了 Map 和 Iterator 接口的所有可选 方法。 

(14).此类与 Hashtable 相似,但与 HashMap 不同,它不 允许将 null 用作键或值。 

 

2.ConcurrentSkipListMap

(1).可缩放的并发 ConcurrentNavigableMap 实现。

(2).映射可以根据键的自然顺序进行排序,也可以根据创建映射时所提供的 Comparator 进行排序,具体取决于使用的构造方法。 

(3).此类实现 SkipLists 的并发变体,为 containsKey、get、put、remove 操作及其变体提供预期平均 log(n) 时间开销。

(4).多个线程可以安全地并发执行插入、移除、更新和访问操作。

(5).迭代器是弱一致 的,返回的元素将反映迭代器创建时或创建后某一时刻的映射状态。它们不 抛出 ConcurrentModificationException,可以并发处理其他操作。

(6).升序键排序视图及其迭代器比降序键排序视图及其迭代器更快。 

(7).此类及此类视图中的方法返回的所有 Map.Entry 对,表示他们产生时的映射关系快照。它们不 支持 Entry.setValue 方法。(注意,根据所需效果,可以使用 put、putIfAbsent 或 replace 更改关联映射中的映射关系。) 

(8).请注意,与在大多数 collection 中不同,这里的 size 方法不是 一个固定时间 (constant-time) 操作。因为这些映射的异步特性,确定元素的当前数目需要遍历元素。

(9).批量操作 putAll、equals 和 clear 并不 保证能以原子方式 (atomically) 执行。例如,与 putAll 操作一起并发操作的迭代器只能查看某些附加元素。 

(10).此类及其视图和迭代器实现 Map 和 Iterator 接口的所有可选 方法。

(11).与大多数其他并发 collection 一样,此类不 允许使用 null 键或值,因为无法可靠地区分 null 返回值与不存在的元素值。 

 

3.ConcurrentSkipListSet

(1).一个基于 ConcurrentSkipListMap 的可缩放并发 NavigableSet 实现。

(2).set 的元素可以根据它们的自然顺序进行排序,也可以根据创建 set 时所提供的 Comparator 进行排序,具体取决于使用的构造方法。 

(3).此实现为 contains、add、remove 操作及其变体提供预期平均 log(n) 时间开销。

(4).多个线程可以安全地并发执行插入、移除和访问操作。

(5).迭代器是弱一致 的,返回的元素将反映迭代器创建时或创建后某一时刻的 set 状态。它们不 抛出 ConcurrentModificationException,可以并发处理其他操作。

(6).升序排序视图及其迭代器比降序排序视图及其迭代器更快。 

(7).请注意,与在大多数 collection 中不同,这里的 size 方法不是 一个固定时间 (constant-time) 操作。由于这些 set 的异步特性,确定元素的当前数目需要遍历元素。

(8).批量操作 addAll、removeAll、retainAll 和 containsAll 并不 保证能以原子方式 (atomically) 执行。例如,与 addAll 操作一起并发操作的迭代器只能查看某些附加元素。 

(9).此类及其迭代器实现 Set 和 Iterator 接口的所有可选 方法。

(10).与大多数其他并发 collection 实现一样,此类不允许使用 null 元素,因为无法可靠地将 null 参数及返回值与不存在的元素区分开来。 

 

4.CopyOnWriteArrayList

(1).ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。 

(2).这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更 有效。

(3).在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时,它也很有用。

(4).“快照”风格的迭代器方法在创建迭代器时使用了对数组状态的引用。

(5).此数组在迭代器的生存期内不会更改,因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。

(6).创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改。

(7).在迭代器上进行的元素更改操作(remove、set 和 add)不受支持。这些方法将抛出 UnsupportedOperationException。 

(8).允许使用所有元素,包括 null。 

(9).内存一致性效果:当存在其他并发 collection 时,将对象放入 CopyOnWriteArrayList 之前的线程中的操作 happen-before 随后通过另一线程从 CopyOnWriteArrayList 中访问或移除该元素的操作。 

 

5.CopyOnWriteArraySet

(1).对其所有操作使用内部 CopyOnWriteArrayList 的 Set。

(2).它最适合于具有以下特征的应用程序:set 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。 

(3).它是线程安全的。 

(4).因为通常需要复制整个基础数组,所以可变操作(add、set 和 remove 等等)的开销很大。 

(5).迭代器不支持可变 remove 操作。 

(6).使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值