我知道像Hashtable这样的集合是同步的,但有人能向我解释一下它是如何工作的,以及在什么时候访问被限制为并发调用?例如,假设我像这样使用一些迭代器:
Hashtable<Integer,Integer> map = new Hashtable<Integer,Integer>();
void dosomething1(){
for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
// do something
}
}
void dosomething2(){
for (Iterator<Map.Entry<Integer,Integer>> i = map.entrySet().iterator(); i.hasNext();){
// do something
// and remove it
i.remove();
}
}
void putsomething(int a, int b){
map.put(a,b);
}
void removesomething(int a){
map.remove(a);
}
var clear(){
map = new Hashtable<Integer,Integer>();
}
有没有人能解释一下我从不同的线程随机调用这些函数有没有什么缺陷?特别是迭代器如何进行同步,特别是当它使用entrySet()时,这似乎也需要同步?如果当其中一个循环正在进行时调用clear()会发生什么?如果removesomething()移除了一个尚未被dosomething1()中的并发循环处理的项,该怎么办?
谢谢你的帮助!
发布于 2009-11-21 23:13:20
在Java语言中,集合上的迭代不是线程安全的,即使您使用的是同步包装器(Collections.synchronizedMap(...)
):
在迭代任何集合视图时,用户必须手动同步返回的映射:
Map m= Collections.synchronizedMap(new HashMap());...设置s= m.keySet();//不需要在synchronized块中...已同步(M){ //正在同步m,而不是% s!迭代器i= s.iterator();//必须在同步块while (i.hasNext()) foo(i.next());}
Java Collection Framework docs
对同步集合的其他调用是安全的,因为包装器类使用synchronized
块围绕它们,这些块使用包装器集合作为它们的监视器:
public int size() {
synchronized( this ) {
return collection.size();
}
}
其中collection
是原始集合。这适用于集合/映射公开的所有方法,但迭代除外。
映射的键集是以同样的方式进行同步的:同步的包装器根本不返回原始键集。相反,它返回集合的原始键集的特殊同步包装。这同样适用于项集和值集。
发布于 2019-01-06 06:01:59
我知道像哈希表这样的集合是同步的
哈希表的条目集使用一种SynchronizedCollection类型的SynchronizedSet。
如果在使用迭代器时修改任何同步或不同步的集合,迭代器将抛出ConcurrentModificationException。
迭代器是作用于集合的对象,在构造过程中给定集合的状态。这使您可以决定何时要查看集合中的下一项。您必须在您知道不会被修改的集合上使用迭代器,或者只计划使用迭代器进行修改。
抛出ConcurrentModificationException的原因是迭代器对集合的当前修改计数进行检查,如果它与期望值不匹配,就会抛出异常。每次添加或删除某些内容时,所有集合都会递增一个修改计数变量。
特别是在使用entrySet()时,
迭代器是如何进行同步的?
因此,迭代器的并不执行同步,当您期望其他线程(或迭代器外部的当前线程)修改集合时,使用它是不安全的。
但是,SynchronizedCollection确实提供了一种同步遍历集合的方法。它的forEach方法的实现是synchronized。
public void forEach(Consumer<? super E> consumer)
请记住,forEach使用了一个增强的for循环,该循环在内部使用迭代器。这意味着forEach仅用于查看集合的内容,而不是用于在查看时对其进行修改。否则将抛出ConcurrentModificationException。
是否有人可以向我解释它是如何工作的,以及在什么情况下访问被限制为并发调用
如果线程想要使用同步的方法,比如(add,remove,forEach),SynchronizedCollection会让线程轮流访问集合。
它的工作原理是引入一个与以下代码类似的synchronized块:
public boolean add(Object o) {
synchronized(this) {
super.add(o);
}
}
在可以对集合执行的所有操作周围引入了synchronized块,以下方法除外:
iterator(), spliterator(), stream(), parallelStream()
https://stackoverflow.com/questions/1775717
复制相似问题