java 解释使用迭代器时集合的同步?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1775717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 17:56:12  来源:igfitidea点击:

Explain synchronization of collections when iterators are used?

javacollectionssynchronization

提问by DivideByHero

I understand that collections like the Hashtable are synchronized, but can someone explain to me howit works, and at what point(s) access is restricted to concurrent calls? For example, let's say I use some iterators like this:

我知道像 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>();
}

Can someone please explain if there are any pitfalls with me calling these functions at random from different threads? How does the iterator, in particular, do its synchronization, especially when it is using entrySet(), which would seem to also require synchronization? What happens if clear() is called while one of the loops is in progress? What if removesomething() removes an item that is not yet processed by a concurrent loop in dosomething1() ?

有人可以解释一下我从不同的线程随机调用这些函数是否有任何陷阱?特别是迭代器如何进行同步,尤其是当它使用 entrySet() 时,这似乎也需要同步?如果在循环之一正在进行时调用 clear() 会发生什么?如果 removesomething() 删除了尚未由 dosomething1() 中的并发循环处理的项目怎么办?

Thanks for any help!

谢谢你的帮助!

回答by Dirk

Iteration over collections in Java is not thread safe, even if you are using one of the synchronized wrappers (Collections.synchronizedMap(...)):

Java 中的集合迭代不是线程安全的,即使您使用的是同步包装器 ( Collections.synchronizedMap(...)) 之一:

It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views:

Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet();  // Needn't be in synchronized block
...
synchronized(m) {  // Synchronizing on m, not s!
    Iterator i = s.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

在迭代任何集合视图时,用户必须在返回的地图上手动同步:

Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet();  // Needn't be in synchronized block
...
synchronized(m) {  // Synchronizing on m, not s!
    Iterator i = s.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

Java Collection Framework docs

Java 集合框架文档

Other calls to synchronized collections are safe, as the wrapper classes surround them with synchronizedblocks, which use the wrapper collection as their monitor:

其他对同步集合的调用是安全的,因为包装类用synchronized块包围它们,这些块使用包装集合作为它们的监视器:

public int size() {
    synchronized( this ) {
        return collection.size();
    }
}

with collectionbeing the original collection. This works for all methods exposed by a collection/map, except for the iteration stuff.

collection最初的原始集合。这适用于集合/映射公开的所有方法,迭代内容除外。

The key set of a map is made synchronized just the same way: the synchronized wrapper does not return the original key set at all. Instead, it returns a special synchronized wrapper of the collection's original key set. The same applies to the entry set and the value set.

映射的键集以同样的方式同步:同步包装器根本不返回原始键集。相反,它返回集合原始键集的特殊同步包装。这同样适用于条目集和值集。

回答by CausingUnderflowsEverywhere

I understand that collections like the Hashtable are synchronized

我知道像 Hashtable 这样的集合是同步的

The HashTable's entry set uses a SynchronizedSet which is a type of SynchronizedCollection.

HashTable 的条目集使用 SynchronizedSet,它是一种 SynchronizedCollection。

If you modify any collection synchronized or not while using an iterator on it, the iterator will throw a ConcurrentModificationException.

如果在使用迭代器时修改任何同步或未同步的集合,迭代器将抛出 ConcurrentModificationException。

An iterator is an Object that acts on a collection, being given the collection's state during construction. This lets you decide when you want to see the next item in the collection, if ever. You must use an iterator on a collection you know won't be modified, or only plan to modify using the iterator.

迭代器是一个作用于集合的对象,在构造过程中被赋予集合的状态。这让您可以决定何时查看集合中的下一个项目(如果有的话)。您必须在您知道不会被修改的集合上使用迭代器,或者只计划使用迭代器进行修改。

The reason ConcurrentModificationException is thrown is because of a check iterators make on the collection's current modification count, if it doesn't match the expected value, the exception is thrown. All collections increment a modification count variable each time something is added or removed.

抛出 ConcurrentModificationException 的原因是因为迭代器对集合的当前修改计数进行了检查,如果它与预期值不匹配,则抛出异常。每次添加或删除某些内容时,所有集合都会增加一个修改计数变量。

How does the iterator, in particular, do its synchronization, especially when it is using entrySet()

迭代器是如何进行同步的,尤其是当它使用 entrySet() 时

So the iterator doesn't do synchronizationand is not safe to use when you expect the collection to be modified by other threads, (or the current thread outside of the iterator).

因此当您希望集合被其他线程(或迭代器之外的当前线程)修改时,迭代器不进行同步并且使用起来不安全。

However, SynchronizedCollectiondoes provide a way to go though the collection synchronously. Its implementation of the forEach method is synchronized.

但是,SynchronizedCollection确实提供了一种同步浏览集合的方法。它对forEach 方法的实现是同步的

public void forEach(Consumer<? super E> consumer)

Just keep in mind, forEach uses an enhanced for loop which uses an iterator internally. This means forEach is only for reviewing the collection's contents, not for modifying it while looking through. Otherwise ConcurrentModificationException will be thrown.

请记住,forEach 使用增强的 for 循环,该循环在内部使用迭代器。这意味着 forEach 仅用于查看集合的内容,而不用于在浏览时对其进行修改。否则将抛出 ConcurrentModificationException。

can someone explain to me how it works, and at what point(s) access is restricted to concurrent calls

有人可以向我解释它是如何工作的,以及在什么时候访问仅限于并​​发调用

SynchronizedCollection causes threads to take turns accessing the collection if they want to use the synchronized methods such as (add, remove, forEach).

如果线程想要使用同步方法(例如(add、remove、forEach)),则 SynchronizedCollection 会导致线程轮流访问集合。

It works by introducing a synchronized block similar to how it's shown in the following code:

它通过引入一个类似于以下代码中显示的同步块来工作:

public boolean add(Object o) {
  synchronized(this) {
  super.add(o);
  }
}

A synchronized block is introduced around all of the operations you can perform on the collection except for the following methods:

围绕您可以对集合执行的所有操作引入了同步块,但以下方法除外:

iterator(), spliterator(), stream(), parallelStream()

Java Official Documentation

Java官方文档