Java理论与实践: 并发集合类 - 编程入门网
DOM树,以及许多其他类型的数据。cache的主要用途是重用前一次处理得 出的结果以减少服务时间和增加吞吐量。cache工作负载的一个典型的特征就是 检索大大多于更新,因此(理想情况下)cache能够提供非常好的 get() 性能。 不过,使用会妨碍性能的cache还不如完全不用cache。
如果使用 synchronizedMap 来实现一个cache,那么您就在您的应用程序中 引入了一个潜在的可伸缩性瓶颈。因为一次只有一个线程可以访问 Map ,这些 线程包括那些要从 Map 中取出一个值的线程以及那些要将一个新的 (key, value) 对插入到该map中的线程。 减小锁粒度 提高 HashMap 的并发性同时还提供线程安全性的一种方法是废除对整个表使 用一个锁的方式,而采用对hash表的每个bucket都使用一个锁的方式(或者,更 常见的是,使用一个锁池,每个锁负责保护几个bucket)。这意味着多个线程可 以同时地访问一个 Map 的不同部分,而不必争用单个的集合范围的锁。这种方 法能够直接提高插入、检索以及移除操作的可伸缩性。不幸的是,这种并发性是 以一定的代价换来的――这使得对整个集合进行操作的一些方法(例如 size() 或 isEmpty() )的实现更加困难,因为这些方法要求一次获得许多的锁,并且 还存在返回不正确的结果的风险。然而,对于某些情况,例如实现cache,这样 做是一个很好的折衷――因为检索和插入操作比较频繁,而 size() 和 isEmpty() 操作则少得多。 ConcurrentHashMap util.concurrent 包中的 ConcurrentHashMap 类(也将出现在JDK 1.5中的 java.util.concurrent 包中)是对 Map 的线程安全的实现,比起 synchronizedMap 来,它提供了好得多的并发性。多个读操作几乎总可以并发地 执行,同时进行的读和写操作通常也能并发地执行,而同时进行的写操作仍然可 以不时地并发进行(相关的类也提供了类似的多个读线程的并发性,但是,只允 许有一个活动的写线程) 。ConcurrentHashMap 被设计用来优化检索操作;实 际上,成功的 get() 操作完成之后通常根本不会有锁着的资源。要在不使用锁 的情况下取得线程安全性需要一定的技巧性,并且需要对Java内存模型(Java Memory Model)的细节有深入的理解。 ConcurrentHashMap 实现,加上 util.concurrent 包的其他部分,已经被研究正确性和线程安全性的并发专家所 正视。在下个月的文章中,我们将看看 ConcurrentHashMap 的实现的细节。 ConcurrentHashMap 通过稍微地松弛它对调用者的承诺而获得了更高的并发 性。检索操作将可以返回由最近完成的插入操作所插入的值,也可以返回在步调 上是并发的插入操作所添加的值(但是决不会返回一个没有意义的结果)。由 ConcurrentHashMap.iterator() 返回的 Iterators 将每次最多返回一个元素, 并且决不会抛出 ConcurrentModificationException 异常,但是可能会也可能 不会反映在该迭代器被构建之后发生的插入操作或者移除操作。在对集合进行迭 代时,不需要表范围的锁就能提供线程安全性。在任何不依赖于锁整个表来防止 更新的应用程序中,可以使用 ConcurrentHashMap 来替代 synchronizedMap 或 Hashtable 。 上述改进使得 ConcurrentHashMap 能够提供比 Hashtable 高得多的可伸缩 性,而且,对于很多类型的公用案例(比如共享的cache)来说,还不用损失其 效率。 Java理论与实践: 并发集合类(3)时间:2010-12-21 IBM Brian Goetz好了多少? 表 1对 Hashtable 和 ConcurrentHashMap 的可伸缩性进行了粗略的比较。 在每次运行过程中, n 个线程并发地执行一个死循环,在这个死循环中这些线 程从一个 Hashtable 或者 ConcurrentHashMap 中检索随机的key value,发现 在执行 put() 操作时有80%的检索失败率,在执行操作时有1%的检索成功率。测 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |