Java理论与实践: 并发调集类[Java编程]
本文“Java理论与实践: 并发调集类[Java编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
在Java类库中呈现的第一个关联的调集类是 Hashtable ,它是JDK 1.0的一 部份. Hashtable 供应了一种易于利用的、线程安全的、关联的map功效,这当 然也是便利的.但是,线程安全性是凭代价换来的―― Hashtable 的全部办法 都是同步的.此时,无竞争的同步会招致可观的性能代价. Hashtable 的后继 者 HashMap 是作为JDK1.2中的调集框架的一部份呈现的,它通过供应一个差别 步的基类和一个同步的包装器 Collections.synchronizedMap ,办理了线程安 全性问题.通过将基本的功效从线程安全性中别脱离来, Collections.synchronizedMap 答应需求同步的用户可以拥有同步,而不需求同 步的用户则没必要为同步付出代价.
Hashtable 和 synchronizedMap 所采纳的得到同步的简单办法(同步 Hashtable 中大概同步的 Map 包装器对象中的每个办法)有两个主要的不足. 首先,这种办法关于可伸缩性是一种障碍,因为一次只能有一个线程可以拜候 hash表.同时,这样仍不足以供应真正的线程安全性,很多公用的混合操作仍旧 需求额外的同步.固然诸如 get() 和 put() 之类的简单操作可以在不需求额外 同步的情形下安全地完成,但还是有一些公用的操作序列,比方迭代大概put- if-absent(空则放入),需求外部的同步,以避免数据争用.
有条件的线程安全性
同步的调集包装器 synchronizedMap 和 synchronizedList ,有时也被称作 有条件地线程安全――全部单个的操作都是线程安全的,但是多个操作构成的操 作序列却大概招致数据争用,因为在操作序列中掌握流取决于前面操作的后果. 清单1中第一片段展示了公用的put-if-absent语句块――假如一个条目不在 Map 中,那么增添这个条目.不幸的是,在 containsKey() 办法返回到 put() 办法 被调用这段时间内,大概会有另一个线程也插入一个带有相同键的值.假如您想 确保只有一次插入,您需求用一个对 Map m 举行同步的同步块将这一对语句包 装起来.
清单1中其他的例子与迭代有关.在第一个例子中, List.size() 的后果在 循环的履行期间大概会变得无效,因为另一个线程可以从这个列表中删除条目. 假如机会不得当,在刚好进入循环的最后一次迭代之后有一个条目被另一个线程 删除了,则 List.get() 将返回 null ,而 doSomething() 则极大概会抛出一 个 NullPointerException 非常.那么,采纳什么办法才能避免这种情形呢?如 果当您正在迭代一个 List 时另一个线程也大概正在拜候这个 List ,那么在进 行迭代时您必须利用一个 synchronized 块将这个 List 包装起来,在 List 1 上同步,从而锁住整个 List .这样做固然办理了数据争用问题,但是在并发性 方面付出了更多的代价,因为在迭代期间锁住整个 List 会阻塞其他线程,使它 们在很长一段时间内不能拜候这个列表.
调集框架引入了迭代器,用于遍历一个列表大概其他调集,从而优化了对一 个调集合的元素举行迭代的历程.但是,在 java.util 调集类中实现的迭代器 极易崩溃,也就是说,假如在一个线程正在通过一个 Iterator 遍历集合时,另 一个线程也来改正这个调集,那么接下来的 Iterator.hasNext() 或 Iterator.next() 调用将抛出 ConcurrentModificationException 非常.就拿 方才这个例子来说,假如想要避免呈现 ConcurrentModificationException 异 常,那么当您正在举行迭代时,您必须利用一个在 List l 上同步的 synchronized 块将该 List 包装起来,从而锁住整个 List .(大概,您也可 以调用 List.toArray() ,在差别步的情形下对数组举行迭代,但是假如列表比 较大的话这样做代价很高).
清单 1. 同步的map中的公用竞争条件
Map m = Collections.synchronizedMap(new HashMap());
List l = Collections.synchronizedList(new ArrayList());
// put-if-absent idiom -- contains a race condition
// may require external synchronization
if (!map.containsKey(key))
map.put(key, value);
// ad-hoc iteration -- contains race conditions
// may require external synchronization
for (int i=0; i<list.size(); i++) {
doSomething(list.get(i));
}
// normal iteration -- can throw ConcurrentModificationException
// may require external synchronization
for (Iterator i=list.iterator(); i.hasNext(); ) {
doSomething(i.next());
}
信任的错觉
synchronizedList 和 synchronizedMap 供应的有条件的线程安全性也带来 了一个隐患 ―― 开辟者会假定,因为这些调集都是同步的,所以它们都是线程 安全的,这样一来他们关于精确地同步混合操作这件事就会忽视.后来果是固然 表面上这些程序在负载较轻的时刻可以正常工作,但是一旦负载较重,它们就会 开始抛出 NullPointerException 或 ConcurrentModificationException .
以上是“Java理论与实践: 并发调集类[Java编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |