java 中的List,Set,Map,Queue的线程问题

Collections和Concurrent都是java用来辅助<? extends Set>和<? extends Collection>的工具类,旨在完成某些特殊而容易重复的任务
,或者是一些比较那已解决的问题。在java中,经常使用的数据结构基本分为3大类,Map,List,Set。

线程安全集合
    JDK 1.2 中引入的 Collection 框架是一种表示对象集合的高度灵活的框架,它使用基本接口 List、Set 和 Map。经过 JDK 提供每一个集合的屡次实现(HashMap、Hashtable、TreeMap、WeakHashMap、HashSet、TreeSet、Vector、ArrayList、LinkedList 等等)。其中一些集合已是线程安全的(Hashtable 和 Vector),经过同步的封装工厂(Collections.synchronizedMap()、synchronizedList() 和 synchronizedSet()),其他的集合都可表现为线程安全的。
    java.util.concurrent 包添加了多个新的线程安全集合类(ConcurrentHashMap、CopyOnWriteArrayList 和 CopyOnWriteArraySet)。这些类的目的是提供高性能、高度可伸缩性、线程安全的基本集合类型版本。
    java.util 中的线程集合仍有一些缺点。例如,在迭代锁定时,一般须要将该锁定保留在集合中,不然,会有抛出 ConcurrentModificationException 的危险。(这个特性有时称为条件线程安全;有关的更多说明,请参阅参考资料。)此外,若是从多个线程频繁地访问集合,则经常不能很好地执行这些类。java.util.concurrent 中的新集合类容许经过在语义中的少许更改来得到更高的并发。
    JDK 5.0 还提供了两个新集合接口 -- Queue 和 BlockingQueue。Queue 接口与 List 相似,但它只容许从后面插入,从前面删除。经过消除 List 的随机访问要求,能够建立比现有 ArrayList 和 LinkedList 实现性能更好的 Queue 实现。由于 List 的许多应用程序实际上不须要随机访问,因此Queue 一般能够替代 List,来得到更好的性能。

CopyOnWriteArrayList 和 CopyOnWriteArraySet
    能够用两种方法建立线程安全支持数据的 List -- Vector 或封装 ArrayList 和 Collections.synchronizedList()。java.util.concurrent 包添加了名称繁琐的 CopyOnWriteArrayList。为何咱们想要新的线程安全的List类?为何Vector还不够?
    最简单的答案是与迭代和并发修改之间的交互有关。使用 Vector 或使用同步的 List 封装器,返回的迭代器是 fail-fast 的,这意味着若是在迭代过程当中任何其余线程修改 List,迭代可能失败。
Vector 的很是广泛的应用程序是存储经过组件注册的监听器的列表。当发生适合的事件时,该组件将在监听器的列表中迭代,调用每一个监听器。为了防止 ConcurrentModificationException,迭代线程必须复制列表或锁定列表,以便进行总体迭代,而这两种状况都须要大量的性能成本。
CopyOnWriteArrayList 类经过每次添加或删除元素时建立支持数组的新副本,避免了这个问题,可是进行中的迭代保持对建立迭代器时的当前副本进行操做。虽然复制也会有一些成本,可是在许多状况下,迭代要比修改多得多,在这些状况下,写入时复制要比其余备用方法具备更好的性能和并发性。
若是应用程序须要 Set 语义,而不是 List,那么还有一个 Set 版本 -- CopyOnWriteArraySet。

ConcurrentHashMap
    正如已经存在线程安全的 List 的实现,您能够用多种方法建立线程安全的、基于 hash 的 Map -- Hashtable,并使用 Collections.synchronizedMap() 封装 HashMap。JDK 5.0 添加了 ConcurrentHashMap 实现,该实现提供了相同的基本线程安全的 Map 功能,但它大大提升了并发性。
    Hashtable 和 synchronizedMap 所采起的得到同步的简单方法(同步 Hashtable 中或者同步的 Map 封装器对象中的每一个方法)有两个主要的不足。首先,这种方法对于可伸缩性是一种障碍,由于一次只能有一个线程能够访问 hash 表。同时,这样仍不足以提供真正的线程安全性,许多公用的混合操做仍然须要额外的同步。虽然诸如 get() 和 put() 之类的简单操做能够在不须要额外同步的状况下安全地完成,但仍是有一些公用的操做序列,例如迭代或者 put-if-absent(空则放入),须要外部的同步,以免数据争用。
    Hashtable 和 Collections.synchronizedMap 经过同步每一个方法得到线程安全。这意味着当一个线程执行一个 Map 方法时,不管其余线程要对 Map 进行什么样操做,都不能执行,直到第一个线程结束才能够。
    对比来讲,ConcurrentHashMap 容许多个读取几乎老是并发执行,读和写操做一般并发执行,多个同时写入常常并发执行。结果是当多个线程须要访问同一 Map 时,能够得到更高的并发性。
    在大多数状况下,ConcurrentHashMap 是 Hashtable或 Collections.synchronizedMap(new HashMap()) 的简单替换。然而,其中有一个显著不一样,即 ConcurrentHashMap 实例中的同步不锁定映射进行独占使用。实际上,没有办法锁定 ConcurrentHashMap 进行独占使用,它被设计用于进行并发访问。为了使集合不被锁定进行独占使用,还提供了公用的混合操做的其余(原子)方法,如 put-if-absent。ConcurrentHashMap 返回的迭代器是弱一致的,意味着它们将不抛出ConcurrentModificationException ,将进行"合理操做"来反映迭代过程当中其余线程对 Map 的修改。

队列
    原始集合框架包含三个接口:List、Map 和 Set。List 描述了元素的有序集合,支持彻底随即访问 -- 能够在任何位置添加、提取或删除元素。
    LinkedList 类常常用于存储工做元素(等待执行的任务)的列表或队列。然而,List 提供的灵活性比该公用应用程序所须要的多得多,这个应用程序一般在后面插入元素,从前面删除元素。可是要支持完整 List 接口则意味着 LinkedList 对于这项任务不像原来那样有效。Queue 接口比 List 简单得多,仅包含 put() 和 take() 方法,并容许比 LinkedList 更有效的实现。
    Queue 接口还容许实现来肯定存储元素的顺序。ConcurrentLinkedQueue 类实现先进先出(first-in-first-out,FIFO)队列,而 PriorityQueue 类实现优先级队列(也称为堆),它对于构建调度器很是有用,调度器必须按优先级或预期的执行时间执行任务。
interface Queue extends Collection {
    boolean offer(E x);
    E poll();
    E remove() throws NoSuchElementException;
    E peek();
    E element() throws NoSuchElementException;
}
实现 Queue 的类是:
    • LinkedList 已经进行了改进来实现 Queue。
    • PriorityQueue 非线程安全的优先级对列(堆)实现,根据天然顺序或比较器返回元素。
    • ConcurrentLinkedQueue 快速、线程安全的、无阻塞 FIFO 队列。

Hashtable 与 ConcurrentHashMap
    做为可伸缩性的例子,ConcurrentHashMap 实现设计的可伸缩性要比其线程安全的上一代 Hashtable 的可伸缩性强得多。Hashtable 一次只容许一个线程访问 Map;ConcurrentHashMap 容许多个读者并发执行,读者与写入者并发执行,以及一些写入者并发执行。所以,若是许多线程频繁访问共享映射,使用 ConcurrentHashMap 的总的吞吐量要比使用 Hashtable 的好。
    下表大体说明了 Hashtable 和 ConcurrentHashMap 之间的可伸缩性差异。在每次运行时,N 个线程并发执行紧密循环,它们从 Hashtable 或 ConcurrentHashMap 中检索随即关键字,60% 的失败检索将执行 put() 操做,2% 的成功检索执行 remove() 操做。测试在运行 Linux 的双处理器 Xeon 系统中执行。数据显示 10,000,000 个迭代的运行时间,对于 ConcurrentHashMap,标准化为一个线程的状况。能够看到直到许多线程,ConcurrentHashMap 的性能仍保持可伸缩性,而 Hashtable 的性能在出现锁定竞争时几乎当即降低。
    与一般的服务器应用程序相比,这个测试中的线程数看起来不多。然而,由于每一个线程未进行其余操做,仅是重复地选择使用该表,因此这样能够模拟在执行一些实际工做的状况下使用该表的大量线程的竞争。

本文内容摘自:http://www.cnblogs.com/sarafill/archive/2011/05/18/2049461.htmlhtml

总结:具体来讲,Vector,Hashtable在操做上是线程安全的,但在遍历时线程并不安全,同理Collections.synchronizedMap()、synchronizedList() 和 synchronizedSet()虽然性能比前者有所提升,但遍历时仍然须要和前者同样加锁才行。ConcurrentHashMap、CopyOnWriteArrayList 和 CopyOnWriteArraySet,ConcurrentLinkedQueue 性能和线程安全上有很大改进,遍历时线程安全。java

相关文章
相关标签/搜索