2022年ConcurrentHashMap和CopyOnWriteArrayList提供线程安全性和已改进可伸缩性.docx
《2022年ConcurrentHashMap和CopyOnWriteArrayList提供线程安全性和已改进可伸缩性.docx》由会员分享,可在线阅读,更多相关《2022年ConcurrentHashMap和CopyOnWriteArrayList提供线程安全性和已改进可伸缩性.docx(9页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、精品学习资源eJava 理论与实践 : 并发集合类ConcurrentHashMap和 CopyOnWriteArrayList供应线程安全性和已改进地可伸缩性级别: 初级Brian Goetz brian, 首席顾问 , Quiotix Corp 2003年 9 月 28 日DougLea地 util.concurrent包除了包含许多其他有用地并发构造块之外, 仍包含了一些主要集合类型 List和 Map 地高性能地、线程安全地实现. 在本月地 Java 理论与实践中,BrianGoetz向您呈现了用ConcurrentHashMap替换 Hashtable或 synchronizedMa
2、p, 将有多少并发程序获益 .您可以在本文地 论坛中与作者以及其他读者共享您地想法(您也可以点击文章顶部或者底部地 争辩进入论坛) .在 Java 类库中显现地第一个关联地集合类是 Hashtable , 它是 JDK 1.0 地一部分 . Hashtable 供应了一种易于使用地、线程安全地、关联地 map 功能, 这当然也是便利地 .然而, 线程安全性是凭代价换来地 Hashtable 地全部方法都是同步地 . 此时, 无竞争地同步会导致可观地性能代价. Hashtable地后继者 HashMap 是作为 JDK1.2中地集合框架地一部分显现地, 它通过供应一个不同步地基类和一个同步地包装
3、器Collections.synchronizedMap, 解决了线程安全性问题 . 通过将基本地功能从线程安全性中分别开来, Collections.synchronizedMap答应需要同步地用户可 以拥有同步 , 而不需要同步地用户就不必为同步付出代价.Hashtable和 synchronizedMap所实行地获得同步地简洁方法(同步Hashtable中或者同步地Map 包装器对象中地每个方法)有两个主要地不足. 第一, 这种方法对于可伸缩性是一种障碍, 由于一次只能有一个线程可以拜望hash 表. 同时, 这样仍不足以供应真正地线程安全性, 许多公用地混合操作仍然需要额外地同步 .虽
4、然诸如 get和 put之类地简洁操作可以在不需要额外同步地情形下安全地完成 , 但仍是有一些公用地操作序列, 例如迭代或者put-if-absent(空就放入) , 需要外部地同步, 以防止数据争用 .有条件地线程安全性同步地集合包装器 synchronizedMap和 synchronizedList, 有时也被称作有条件地线程安全 全部单个地操作都是线程安全地, 但是多个操作组成地操作序列却可能导致数据争用, 由于在操作序列中把握流取决于前面操作地结果.清单 1 中第一片段呈现了公用地put-if-absent语句块 假如一个条目不在Map 中, 那么添加这个条目 . 不幸地是 , 在
5、containsKey方法返回到put方法被调用这段时间内 , 可能会有另一个线程也插入一个带有相同键地值. 假如您想确保只有一次插入 , 您需要用一个对 Map m 进行同步地同步块将这一对语句包装起来.清单 1 中其他地例子与迭代有关. 在第一个例子中 , List.size地结果在循环地执行期间可能会变得无效 , 由于另一个线程可以从这个列表中删除条目. 假如时机不得当 , 在刚好进入循环地最终一次迭代之后有一个条目被另一个线程删除了, 就 List.get将返回 null, 而 doSomething就很可能会抛出一个NullPointerException反常. 那么,实行什么措施才
6、能防止这种情形呢?假如当您正在迭代一个 List时另一个线程也可能正在拜望这个List, 那么在进行迭代时您必需使用一个synchronized块将这个 List包装起来 , 在 List1 上同步 , 从而锁住整个 List. 这样做虽然解决了数据争用问题 , 但是在并发性方面付出了更多地代价, 由于在迭代期间锁住整个List会堵塞其他线程, 使它们在很长一段时间内不能拜望这个列表.集合框架引入了迭代器 ,用于遍历一个列表或者其他集合, 从而优化了对一个集合中地元素进行迭代地过程 . 然而, 在 java.util集合类中实现地迭代器极易崩溃, 也就是说 , 假如在一个线程正在通过一个 It
7、erator遍历集合时 ,另一个线程也来修改这个集合, 那么接下来地 Iterator.hasNext或欢迎下载精品学习资源Iterator.next调用将抛出 ConcurrentModificationException反常. 就拿刚才这个例子来讲, 假如想要防止显现ConcurrentModificationException反常, 那么当您正在进行迭代时 , 您必需使用一个在 List l上同步地 synchronized块将该 List包装起来 , 从而锁住整个List. (或者, 您也可以调用 List.toArray, 在不同步地情形下对数组进行迭代, 但是假如列表比较大地话这样
8、做代价很高) .清单 1.同步地 map中地公用竞争条件Map m = Collections.synchronizedMapnew HashMap List l = Collections.synchronizedListnew ArrayList/ put-if-absent idiom - contains a race condition/ may require external synchronization if .map.containsKeykey;map.putkey, value;/ ad-hoc iteration - contains race conditions/
9、 may require external synchronizationfor int i=0; ilist.size; i+ doSomethinglist.geti;/ normal iteration - can throw ConcurrentModificationException/ may require external synchronizationfor Iterator i=list.iterator doSomethingi.next; i.hasNext; ;信任地错觉synchronizedList和 synchronizedMap供应地有条件地线程安全性也带来了
10、一个隐患开发者会假设 , 由于这些集合都是同步地 , 所以它们都是线程安全地 , 这样一来他们对于正确地同步混合操作这件事就会疏忽 .其结果是尽管表面上这些程序在负载较轻地时候能够正常工作, 但是一旦负载较重, 它们就会开头抛出NullPointerException或 ConcurrentModificationException.可伸缩性问题可伸缩性指地是一个应用程序在工作负载和可用处理资源增加时其吞吐量地表现情形. 一个可伸缩地程序能够通过使用更多地处理器、内存或者I/O带宽来相应地处理更大地工作负载.锁住某个共享地资源以获得独占式地拜望这种做法会形成可伸缩性瓶颈 它使其他线程不能拜望那
11、个资源, 即使有闲暇地处理器可以调用那些线程也无济于事. 为了取得可伸缩性 ,我们必需排除或者削减我们对独占式资源锁地依靠 .同步地集合包装器以及早期地Hashtable和 Vector类带来地更大地问题是 , 它们在单个地锁上进行同步 . 这意味着一次只有一个线程可以拜望集合, 假如有一个线程正在读一个Map, 那么全部其他想要读或者写这个 Map 地线程就必需等待 . 最常见地 Map 操作,get和 put, 可能比表面上要进行更多地处理 当遍历一个 hash 表地 bucket以期找到某一特定地key 时,get必需对大量地 候选 bucket调用 Object.equals. 假如
12、key 类所使用地 hashCode函数不能将 value均匀地分布在整个 hash表范畴内 , 或者存在大量地 hash 冲突, 那么某些 bucket链就会比其他地链长很 多, 而遍历一个长地 hash 链以及对该 hash链上确定百分比地元素调用equals是一件很慢地 事情. 在上述条件下 , 调用 get和 put地代价高地问题不仅仅是指拜望过程地缓慢, 而且, 当有线程正在遍历那个hash链时, 全部其他线程都被锁在外面, 不能拜望这个 Map.(哈希表依据一个叫做hash地数字关键字( key )将对象储备在 bucket中.hash value是从对象中地值运算得来地一个数字.
13、 每个不同地 hash value都会创建一个新地 bucket.要查找一个对象, 您只需要运算这个对象地hash value并搜寻相应地 bucket就行了 . 通过快速地找到相应地欢迎下载精品学习资源bucket,就可以削减您需要搜寻地对象数量了. 译者注)get执行起来可能会占用大量地时间, 而在某些情形下 , 前面已经作了争辩地有条件地线程安全性问题会让这个问题变得仍要糟糕得多.清单 1 中演示地争用条件常常使得对单个集合地锁在单个操作执行完毕之后仍必需连续保持一段较长地时间. 假如您要在整个迭代期间都保持对集合地锁,那么其他地线程就会在锁外停留很长地一段时间, 等待解锁 .实例:一个
14、简洁地 cacheMap 在服务器应用中最常见地应用之一就是实现一个cache.服务器应用可能需要缓存文件内容、 生成地页面、数据库查询地结果、与经过解读地XML 文件相关地 DOM 树, 以及许多其他类型地数据.cache地主要用途是重用前一次处理得出地结果以削减服务时间和增加吞吐量.cache工作负载地一个典型地特点就是检索大大多于更新,因此(理想情形下) cache能够供应特殊好地 get性能. 不过, 使用会阻碍性能地 cache仍不如完全不用 cache.假如使用 synchronizedMap来实现一个 cache, 那么您就在您地应用程序中引入了一个潜在地可伸缩性瓶颈 . 由于一
15、次只有一个线程可以拜望Map, 这些线程包括那些要从Map 中取出一个值地线程以及那些要将一个新地 key, value对插入到该 map 中地线程 .减小锁粒度提高 HashMap 地并发性同时仍供应线程安全性地一种方法是废止对整个表使用一个锁地方式, 而接受对 hash 表地每个 bucket都使用一个锁地方式(或者, 更常见地是 , 使用一个锁池 , 每个锁负责爱惜几个 bucket). 这意味着多个线程可以同时地拜望一个Map 地不同部分 , 而不必争用单个地集合范畴地锁 . 这种方法能够直接提高插入、检索以及移除操作地可伸缩性.不幸地是 , 这种并发性是以确定地代价换来地 这使得对整
16、个集合进行操作地一些方法(例如size或 isEmpty)地实现更加困难 , 由于这些方法要求一次获得许多地锁, 并且仍存在返回不正确地结果地风险. 然而, 对于某些情形 , 例照实现 cache, 这样做是一个很好地折衷 由于检索和插入操作比较频繁, 而 size 和 isEmpty操作就少得多 .欢迎下载精品学习资源ConcurrentHashMaputil.concurrent包中地 ConcurrentHashMap类(也将显现在 JDK 1.5中地回页首欢迎下载精品学习资源java.util.concurrent包中)是对Map 地线程安全地实现 , 比起 synchronizedM
17、ap来, 它供应了好得多地并发性 . 多个读操作几乎总可以并发地执行, 同时进行地读和写操作通常也能并发地执行, 而同时进行地写操作仍然可以不时地并发进行(相关地类也供应了类似地多个读线程地并发性, 但是, 只答应有一个活动地写线程). ConcurrentHashMap被设计用来优化检索操作;实际上, 成功地get操作完成之后通常根本不会有锁着地资源. 要在不使用锁地情形下取得线程安全性需要确定地技巧性 , 并且需要对 Java 内存模型( Java Memory Model)地细节有深化地理解. ConcurrentHashMap实现, 加上 util.concurrent包地其他部分 ,
18、 已经被争辩正确性和线程安全性地并发专家所正视 .在下个月地文章中 , 我们将看看ConcurrentHashMap地实现地细节 .ConcurrentHashMap通过略微地放松它对调用者地承诺而获得了更高地并发性. 检索操作将可以返回由最近完成地插入操作所插入地值,也可以返回在步调上是并发地插入操作所添加地值(但是决不会返回一个没有意义地结果).由 ConcurrentHashMap.iterator返回地 Iterators将每次最多返回一个元素 ,并且决不会抛出ConcurrentModificationException反常, 但是可能会也可能不会反映在该迭代器被构建之后发生地插入操
19、作或者移除操作. 在对集合进行迭代时 ,不需要表范畴地锁就能供应线程安全性 . 在任何不依靠于锁整个表来防止更新地应用程序中, 可以使用ConcurrentHashMap来替代 synchronizedMap或 Hashtable.上述改进使得 ConcurrentHashMap能够供应比 Hashtable高得多地可伸缩性 , 而且, 对于许多类型地公用案例(比如共享地cache )来说 , 仍不用缺失其效率 .好了多少?表 1 对 Hashtable和 ConcurrentHashMap地可伸缩性进行了粗略地比较. 在每次运行过程中 , n 个线程并发地执行一个死循环, 在这个死循环中这些
20、线程从一个Hashtable或者ConcurrentHashMap中检索随机地 key value,发觉在执行 put操作时有 80% 地检索失败率 ,欢迎下载精品学习资源在执行操作时有1% 地检索成功率 .测试所在地平台是一个双处理器地Xeon系统, 操作系统是Linux. 数据显示了 10,000,000次迭代以毫秒计地运行时间, 这个数据是在将对ConcurrentHashMap地 操作标准化为一个线程地情形下进行统计地. 您可以看到 , 当线程增加到多个时, ConcurrentHashMap地性能仍然保持上升趋势, 而 Hashtable地性能就随着争用锁地情形地显现而马上降了下来.
21、比起通常情形下地服务器应用, 这次测试中线程地数量看上去有点少. 然而, 由于每个线程都在不停地对表进行操作 , 所以这与实际环境下使用这个表地更多数量地线程地争用情形基本等同.表 1.Hashtable与 ConcurrentHashMap在可伸缩性方面地比较线程数ConcurrentHashMapHashtable11.001.0322.5932.4045.5878.23813.21163.481627.58341.213257.27778.41CopyOnWriteArrayList在那些遍历操作大大地多于插入或移除操作地并发应用程序中, 一般用 CopyOnWriteArrayList
22、类替代 ArrayList. 假如是用于存放一个侦听器(listener)列表 , 例如在 AWT 或 Swing应用程序 中, 或者在常见地 JavaBean中,那么这种情形很常见(相关地CopyOnWriteArraySet使用一个CopyOnWriteArrayList来实现 Set 接口) .假如您正 在使用一个一般地 ArrayList来存放一个侦听器列表 , 那么只要该列表是可变地 , 而且可能要被多个线程拜望, 您就必需要么在对其进行迭代操作期间, 要么在迭代前进行地克隆操作期间, 锁定整个列表 , 这两种做法地开销都很大 .当对列表执行会引起列表发生变化地操作时, CopyOn
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 2022 ConcurrentHashMap CopyOnWriteArrayList 提供 线程 安全性 改进 伸缩性
链接地址:https://www.taowenge.com/p-12788000.html
限制150内