ConcurrentHashMap CopyOnWriteArrayList 提供线程安性已改良伸缩性
级 初级
Brian Goetz (brian@quiotix ) 首席参谋 Quiotix Corp
2003 年 9 月 28 日
DougLea utilconcurrent 包包含许发构造块外包含集合类型 List Map 高性线程安实现月 Java理实践中BrianGoetz您展示 ConcurrentHashMap 换 Hashtable synchronizedMap 少发程序获益您文 坛中作者读者享您想法〔您点击文章顶部者底部 讨进入坛〕
Java类库中出现第关联集合类 Hashtable JDK 10局部 Hashtable 提供种易线程安关联map功然方便然线程安性代价换―― Hashtable 方法步时竞争步会导致观性代价 Hashtable 继者 HashMap 作JDK12中集合框架局部出现通提供步基类步包装器 CollectionssynchronizedMap 解决线程安性问题通根功线程安性中离开 CollectionssynchronizedMap 允许需步户拥步需步户必步付出代价
Hashtable synchronizedMap 采取获步简单方法〔步 Hashtable 中者步 Map 包装器象中方法〕两缺乏首先种方法伸缩性种障碍次线程访问hash表时样缺乏提供真正线程安性许公混合操作然需额外步然诸 get() put() 类简单操作需额外步情况安完成公操作序列例迭代者putifabsent〔空放入〕需外部步防止数争
条件线程安性
步集合包装器 synchronizedMap synchronizedList 时称作 条件线程安――单操作线程安操作组成操作序列导致数争操作序列中控制流取决前面操作结果 清单1中第片段展示公putifabsent语句块――果条目 Map 中添加条目幸 containsKey() 方法返回 put() 方法调段时间会线程插入带相键值果您想确保次插入您需 Map m 进行步步块语句包装起
清单1中例子迭代关第例子中 Listsize() 结果循环执行期间会变效线程列表中删条目果时机刚进入循环次迭代条目线程删 Listget() 返回 null doSomething() 会抛出 NullPointerException 异常采取什措施防止种情况呢?果您正迭代 List 时线程正访问 List 进行迭代时您必须 synchronized 块 List 包装起 List 1 步锁住整 List 样做然解决数争问题发性方面付出更代价迭代期间锁住整 List 会阻塞线程长段时间访问列表
集合框架引入迭代器遍历列表者集合优化集合中元素进行迭代程然 javautil 集合类中实现迭代器极易崩溃说果线程正通 Iterator 遍历集合时线程修改集合接 IteratorhasNext() Iteratornext() 调抛出 ConcurrentModificationException 异常刚刚例子讲果想防止出现 ConcurrentModificationException 异常您正进行迭代时您必须 List l 步 synchronized 块该 List 包装起锁住整 List 〔者您调 ListtoArray() 步情况数组进行迭代果列表拟话样做代价高〕
清单 1 步map中公竞争条件
Map m CollectionssynchronizedMap(new HashMap())
List l CollectionssynchronizedList(new ArrayList())
putifabsent idiom contains a race condition
may require external synchronization
if (mapcontainsKey(key))
mapput(key value)
adhoc iteration contains race conditions
may require external synchronization
for (int i0 i
}
normal iteration can throw ConcurrentModificationException
may require external synchronization
for (Iterator ilistiterator() ihasNext() ) {
doSomething(inext())
}
信错觉
synchronizedList synchronizedMap 提供条件线程安性带隐患 ―― 开发者会假设集合步线程安样正确步混合操作件事会疏忽结果外表程序负载较轻时候够正常工作旦负载较重会开始抛出 NullPointerException ConcurrentModificationException
伸缩性问题
伸缩性指应程序工作负载处理资源增加时吞吐量表现情况伸缩程序够通更处理器存者IO带宽相应处理更工作负载锁住某享资源获独占式访问种做法会形成伸缩性瓶颈――线程访问资源空闲处理器调线程济事取伸缩性必须消者减少独占式资源锁赖
步集合包装器早期 Hashtable Vector 类带更问题单锁进行步意味着次线程访问集合果线程正读 Map 想读者写 Map 线程必须等常见 Map 操作 get() put() 外表进行更处理――遍历hash表bucket期找某特定key时 get() 必须量候选bucket调 Objectequals() 果key类 hashCode() 函数value均匀分布整hash表范围者存量hash突某bucket链会链长遍历长hash链该hash链定百分元素调 equals() 件慢事情述条件调 get() put() 代价高问题仅仅指访问程缓慢线程正遍历hash链时线程锁外面访问 Map
〔哈希表根做hash数字关键字〔key〕象存储bucket中hash value象中值计算数字hash value会创立新bucket查找象您需计算象hash value搜索相应bucket行通快速找相应bucket减少您需搜索象数量译者注〕
get() 执行起会占量时间某情况前面已作讨条件线程安性问题会问题变糟糕 清单1 中演示争条件常常单集合锁单操作执行完毕必须继续保持段较长时间果您整迭代期间保持集合锁线程会锁外停留长段时间等解锁
实例:简单cache
Map 效劳器应中常见应实现 cache 效劳器应需缓存文件容生成页面数库查询结果解析XML文件相关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
utilconcurrent 包中 ConcurrentHashMap 类〔出现JDK 15中 包中〕 Map 线程安实现起 synchronizedMap 提供发性读操作总发执行时进行读写操作通常发执行时进行写操作然时发进行〔相关类提供类似读线程发性允许活动写线程〕 ConcurrentHashMap 设计优化检索操作实际成功 get() 操作完成通常根会锁着资源锁情况取线程安性需定技巧性需Java存模型〔Java Memory Model〕细节深入理解 ConcurrentHashMap 实现加 utilconcurrent 包局部已研究正确性线程安性发专家正视月文章中 ConcurrentHashMap 实现细节
ConcurrentHashMap 通稍微松弛调者承诺获更高发性检索操作返回完成插入操作插入值返回步调发插入操作添加值〔决会返回没意义结果〕 ConcurrentHashMapiterator() 返回 Iterators 次返回元素决会抛出 ConcurrentModificationException 异常会会反映该迭代器构建发生插入操作者移操作集合进行迭代时需表范围锁提供线程安性赖锁整表防止更新应程序中 ConcurrentHashMap 代 synchronizedMap Hashtable
述改良 ConcurrentHashMap 够提供 Hashtable 高伸缩性类型公案例〔方享cache〕说损失效率
少?
表 1 Hashtable ConcurrentHashMap 伸缩性进行粗略拟次运行程中 n 线程发执行死循环死循环中线程 Hashtable 者 ConcurrentHashMap 中检索机key value发现执行 put() 操作时80检索失败率执行操作时1检索成功率测试台双处理器Xeon系统操作系统Linux数显示10000000次迭代毫秒计运行时间数 ConcurrentHashMap 操作标准化线程情况进行统计您线程增加时 ConcurrentHashMap 性然保持升趋势 Hashtable 性着争锁情况出现立降
起通常情况效劳器应次测试中线程数量点少然线程停表进行操作实际环境表更数量线程争情况根等
表 1Hashtable ConcurrentHashMap伸缩性方面拟
线程数
ConcurrentHashMap
Hashtable
1
100
103
2
259
3240
4
558
7823
8
1321
16348
16
2758
34121
32
5727
77841
CopyOnWriteArrayList
遍历操作插入移操作发应程序中般 CopyOnWriteArrayList 类代 ArrayList 果存放侦听器〔listener〕列表例AWTSwing应程序中者常见JavaBean中种情况常见〔相关 CopyOnWriteArraySet CopyOnWriteArrayList 实现 Set 接口〕
果您正普通 ArrayList 存放侦听器列表该列表变线程访问您必须进行迭代操作期间迭代前进行克隆操作期间锁定整列表两种做法开销
列表执行会引起列表发生变化操作时 CopyOnWriteArrayList 列表创立全新副迭代器肯定够返回迭代器创立时列表状态会抛出 ConcurrentModificationException 列表进行迭代前必克隆列表者迭代期间锁定列表迭代器列表副变换句话说 CopyOnWriteArrayList 含变数组变引保存引您获变线程安性处锁定列表
结束语
步集合类 Hashtable Vector 步包装器类 CollectionssynchronizedMap CollectionssynchronizedList Map List 提供根条件线程安实现然某素适具高度发性应程序中――集合范围单锁特性伸缩性说障碍时候必须段较长时间锁定集合防止出现 ConcurrentModificationException s异常 ConcurrentHashMap CopyOnWriteArrayList 实现提供更高发性时保住线程安性调者承诺点折扣 ConcurrentHashMap CopyOnWriteArrayList 您 HashMap ArrayList 方定设计优化某特定公解决方案许发应程序中获处
Java 理实践 发集合类
ConcurrentHashMap CopyOnWriteArrayList 提供线程安性已改良伸缩性
级 初级
Brian Goetz () 首席参谋 Quiotix Corp
2003 年 9 月 28 日
DougLea utilconcurrent 包包含许发构造块外包含集合类型 List Map 高性线程安实现月 Java理实践中BrianGoetz您展示 ConcurrentHashMap 换 Hashtable synchronizedMap 少发程序获益您文 坛中作者读者享您想法〔您点击文章顶部者底部 讨进入坛〕
Java类库中出现第关联集合类 Hashtable JDK 10局部 Hashtable 提供种易线程安关联map功然方便然线程安性代价换―― Hashtable 方法步时竞争步会导致观性代价 Hashtable 继者 HashMap 作JDK12中集合框架局部出现通提供步基类步包装器 CollectionssynchronizedMap 解决线程安性问题通根功线程安性中离开 CollectionssynchronizedMap 允许需步户拥步需步户必步付出代价
Hashtable synchronizedMap 采取获步简单方法〔步 Hashtable 中者步 Map 包装器象中方法〕两缺乏首先种方法伸缩性种障碍次线程访问hash表时样缺乏提供真正线程安性许公混合操作然需额外步然诸 get() put() 类简单操作需额外步情况安完成公操作序列例迭代者putifabsent〔空放入〕需外部步防止数争
条件线程安性
步集合包装器 synchronizedMap synchronizedList 时称作 条件线程安――单操作线程安操作组成操作序列导致数争操作序列中控制流取决前面操作结果 清单1中第片段展示公putifabsent语句块――果条目 Map 中添加条目幸 containsKey() 方法返回 put() 方法调段时间会线程插入带相键值果您想确保次插入您需 Map m 进行步步块语句包装起
清单1中例子迭代关第例子中 Listsize() 结果循环执行期间会变效线程列表中删条目果时机刚进入循环次迭代条目线程删 Listget() 返回 null doSomething() 会抛出 NullPointerException 异常采取什措施防止种情况呢?果您正迭代 List 时线程正访问 List 进行迭代时您必须 synchronized 块 List 包装起 List 1 步锁住整 List 样做然解决数争问题发性方面付出更代价迭代期间锁住整 List 会阻塞线程长段时间访问列表
集合框架引入迭代器遍历列表者集合优化集合中元素进行迭代程然 javautil 集合类中实现迭代器极易崩溃说果线程正通 Iterator 遍历集合时线程修改集合接 IteratorhasNext() Iteratornext() 调抛出 ConcurrentModificationException 异常刚刚例子讲果想防止出现 ConcurrentModificationException 异常您正进行迭代时您必须 List l 步 synchronized 块该 List 包装起锁住整 List 〔者您调 ListtoArray() 步情况数组进行迭代果列表拟话样做代价高〕
清单 1 步map中公竞争条件
Map m CollectionssynchronizedMap(new HashMap())
List l CollectionssynchronizedList(new ArrayList())
putifabsent idiom contains a race condition
may require external synchronization
if (mapcontainsKey(key))
mapput(key value)
adhoc iteration contains race conditions
may require external synchronization
for (int i0 i
}
normal iteration can throw ConcurrentModificationException
may require external synchronization
for (Iterator ilistiterator() ihasNext() ) {
doSomething(inext())
}
信错觉
synchronizedList synchronizedMap 提供条件线程安性带隐患 ―― 开发者会假设集合步线程安样正确步混合操作件事会疏忽结果外表程序负载较轻时候够正常工作旦负载较重会开始抛出 NullPointerException ConcurrentModificationException
伸缩性问题
伸缩性指应程序工作负载处理资源增加时吞吐量表现情况伸缩程序够通更处理器存者IO带宽相应处理更工作负载锁住某享资源获独占式访问种做法会形成伸缩性瓶颈――线程访问资源空闲处理器调线程济事取伸缩性必须消者减少独占式资源锁赖
步集合包装器早期 Hashtable Vector 类带更问题单锁进行步意味着次线程访问集合果线程正读 Map 想读者写 Map 线程必须等常见 Map 操作 get() put() 外表进行更处理――遍历hash表bucket期找某特定key时 get() 必须量候选bucket调 Objectequals() 果key类 hashCode() 函数value均匀分布整hash表范围者存量hash突某bucket链会链长遍历长hash链该hash链定百分元素调 equals() 件慢事情述条件调 get() put() 代价高问题仅仅指访问程缓慢线程正遍历hash链时线程锁外面访问 Map
〔哈希表根做hash数字关键字〔key〕象存储bucket中hash value象中值计算数字hash value会创立新bucket查找象您需计算象hash value搜索相应bucket行通快速找相应bucket减少您需搜索象数量译者注〕
get() 执行起会占量时间某情况前面已作讨条件线程安性问题会问题变糟糕 清单1 中演示争条件常常单集合锁单操作执行完毕必须继续保持段较长时间果您整迭代期间保持集合锁线程会锁外停留长段时间等解锁
实例:简单cache
Map 效劳器应中常见应实现 cache 效劳器应需缓存文件容生成页面数库查询结果解析XML文件相关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
utilconcurrent 包中 ConcurrentHashMap 类〔出现JDK 15中 包中〕 Map 线程安实现起 synchronizedMap 提供发性读操作总发执行时进行读写操作通常发执行时进行写操作然时发进行〔相关类提供类似读线程发性允许活动写线程〕 ConcurrentHashMap 设计优化检索操作实际成功 get() 操作完成通常根会锁着资源锁情况取线程安性需定技巧性需Java存模型〔Java Memory Model〕细节深入理解 ConcurrentHashMap 实现加 utilconcurrent 包局部已研究正确性线程安性发专家正视月文章中 ConcurrentHashMap 实现细节
ConcurrentHashMap 通稍微松弛调者承诺获更高发性检索操作返回完成插入操作插入值返回步调发插入操作添加值〔决会返回没意义结果〕 ConcurrentHashMapiterator() 返回 Iterators 次返回元素决会抛出 ConcurrentModificationException 异常会会反映该迭代器构建发生插入操作者移操作集合进行迭代时需表范围锁提供线程安性赖锁整表防止更新应程序中 ConcurrentHashMap 代 synchronizedMap Hashtable
述改良 ConcurrentHashMap 够提供 Hashtable 高伸缩性类型公案例〔方享cache〕说损失效率
少?
表 1 Hashtable ConcurrentHashMap 伸缩性进行粗略拟次运行程中 n 线程发执行死循环死循环中线程 Hashtable 者 ConcurrentHashMap 中检索机key value发现执行 put() 操作时80检索失败率执行操作时1检索成功率测试台双处理器Xeon系统操作系统Linux数显示10000000次迭代毫秒计运行时间数 ConcurrentHashMap 操作标准化线程情况进行统计您线程增加时 ConcurrentHashMap 性然保持升趋势 Hashtable 性着争锁情况出现立降
起通常情况效劳器应次测试中线程数量点少然线程停表进行操作实际环境表更数量线程争情况根等
表 1Hashtable ConcurrentHashMap伸缩性方面拟
线程数
ConcurrentHashMap
Hashtable
1
100
103
2
259
3240
4
558
7823
8
1321
16348
16
2758
34121
32
5727
77841
CopyOnWriteArrayList
遍历操作插入移操作发应程序中般 CopyOnWriteArrayList 类代 ArrayList 果存放侦听器〔listener〕列表例AWTSwing应程序中者常见JavaBean中种情况常见〔相关 CopyOnWriteArraySet CopyOnWriteArrayList 实现 Set 接口〕
果您正普通 ArrayList 存放侦听器列表该列表变线程访问您必须进行迭代操作期间迭代前进行克隆操作期间锁定整列表两种做法开销列表执行会引起列表发生变化操作时 CopyOnWriteArrayList 列表创立全新副迭代器肯定够返回迭代器创立时列表状态会抛出 ConcurrentModificationException 列表进行迭代前必克隆列表者迭代期间锁定列表迭代器列表副变换句话说 CopyOnWriteArrayList 含变数组变引保存引您获变线程安性处锁定列表
结束语
步集合类 Hashtable Vector 步包装器类 CollectionssynchronizedMap CollectionssynchronizedList Map List 提供根条件线程安实现然某素适具高度发性应程序中――集合范围单锁特性伸缩性说障碍时候必须段较长时间锁定集合防止出现 ConcurrentModificationException s异常 ConcurrentHashMap CopyOnWriteArrayList 实现提供更高发性时保住线程安性调者承诺点折扣 ConcurrentHashMap CopyOnWriteArrayList 您 HashMap ArrayList 方定设计优化某特定公解决方案许发应程序中获处
文档香网(httpswwwxiangdangnet)户传
《香当网》用户分享的内容,不代表《香当网》观点或立场,请自行判断内容的真实性和可靠性!
该内容是文档的文本内容,更好的格式请下载文档