在Java类库中出现的第一个关联的集合类是 Hashtable
,它是JDK 1.0的一部分。 Hashtable
提供了一种易于使用的、线程安全的、关联的map功能,确实比较方便。然而,线程安全性是有代价的――Hashtable
的所有方法都是同步的。因此,无竞争的同步会导致可观的性能代价。(从目前参与的项目来看,这种场景较少,因为无论是全局变量或者局部变量,每次请求时,线程都是创建一个prototype对象,实例化一个新的对象。集合类对象不会被多个线程同时使用)
HashMap
是Hashtable
的后继者 ,作为JDK1.2中的集合框架的一部分出现的,它通过提供一个不同步的基类HashMap和一个同步的包装器Collections.synchronizedMap
,解决了线程安全性问题。从而将基本的功能从线程安全性中分离出来,Collections.synchronizedMap
给用户更多的自由选择,如果需要同步的用户可以选择它从而拥有同步,不需要同步的用户则不必为同步付出代价。
狭隘上的线程安全
同步的集合包装器 synchronizedMap
和 synchronizedList
,有时也被称作有条件地线程安全――所有单个的操作都是线程安全的,但是多个操作组成的操作序列却可能导致数据争用,因为在操作序列中控制流取决于前面操作的结果
Map在缓存中的使用
Map
在服务器应用中最常见的应用之一就是实现一个 cache。
服务器应用可能需要缓存文件内容、生成的页面、数据库查询的结果、与经过解析的XML文件相关的DOM树,以及许多其他类型的数据。cache的主要用途是重用前一次处理得出的结果以减少服务时间和增加吞吐量。cache工作负载的一个典型的特征就是检索大大多于更新,因此(理想情况下)cache能够提供非常好的get()
性能。
如果使用 synchronizedMap
来实现一个cache,那么您就在您的应用程序中引入了一个潜在的可伸缩性瓶颈。因为一次只有一个线程可以访问Map
,这些线程包括那些要从Map
中取出一个值的线程以及那些要将一个新的(key, value)
对插入到该map中的线程。
ConcurrentHashMap
util.concurrent
包中的 ConcurrentHashMap
类(也将出现在JDK 1.5中的java.util.concurrent
包中)是对Map
的线程安全的实现,比起synchronizedMap
来,它提供了好得多的并发性。多个读操作几乎总可以并发地执行,同时进行的读和写操作通常也能并发地执行,而同时进行的写操作仍然可以不时地并发进行(相关的类也提供了类似的多个读线程的并发性,但是,只允许有一个活动的写线程)。ConcurrentHashMap
被设计用来优化检索操作;实际上,成功的get()
操作完成之后通常根本不会有锁着的资源。要在不使用锁的情况下取得线程安全性需要一定的技巧性,并且需要对Java内存模型(Java Memory Model)的细节有深入的理解。ConcurrentHashMap
实现,加上util.concurrent
包的其他部分,已经被研究正确性和线程安全性的并发专家所正视。
总结:
同步的集合类 Hashtable
和 Vector
,以及同步的包装器类Collections.synchronizedMap
和Collections.synchronizedList
,为Map
和List
提供了基本的有条件的线程安全的实现。然而,某些因素使得它们并不适用于具有高度并发性的应用程序中――它们的集合范围的单锁特性对于可伸缩性来说是一个障碍,而且,很多时候还必须在一段较长的时间内锁定一个集合,以防止出现ConcurrentModificationException
s异常。ConcurrentHashMap
和CopyOnWriteArrayList
实现提供了更高的并发性,同时还保住了线程安全性,只不过在对其调用者的承诺上打了点折扣。ConcurrentHashMap
和CopyOnWriteArrayList
并不是在您使用HashMap
或ArrayList
的任何地方都一定有用,但是它们被设计用于优化某些特定的公用解决方案。许多并发应用程序将因为使用它们而获得好处。
Reference:
http://www.ibm.com/developerworks/cn/java/j-jtp07233/
http://www.ibm.com/developerworks/cn/java/j-concurrent/#JAVAZ167