day*_*ott 15 java concurrency iterator
public synchronized X getAnotherX(){
if(iterator.hasNext()){
X b = iterator.next();
String name = b.getInputFileName();
...
return b;
}
else{return null;}
}
Run Code Online (Sandbox Code Playgroud)
尽管声明头中的synchronized语句,我仍然在我使用iterator.next()的行中得到一个ConcurrentModificationException异常; 什么错了?
Ram*_*mon 39
ConcurrentModificationException
通常与多线程无关.大多数情况下它会发生,因为您正在修改它在迭代循环体内迭代的集合.例如,这将导致它:
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
Item item = (Item) iterator.next();
if (item.satisfiesCondition()) {
collection.remove(item);
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,您必须使用该iterator.remove()
方法.如果要添加到集合中,则会同样发生这种情况,在这种情况下,没有通用解决方案.但是,ListIterator
如果处理列表并且这有一个add()
方法,则可以使用子类型.
我同意上面关于ConcurrentModificationException
经常发生的陈述,因为在迭代的同一线程中修改集合。然而,这并不总是原因。
需要记住的synchronized
是,它仅在访问共享资源的每个人都同步时才保证独占访问。
例如,您可以同步对共享变量的访问:
synchronized (foo) {
foo.setBar();
}
Run Code Online (Sandbox Code Playgroud)
您可以认为您可以独享它。但是,没有什么可以阻止另一个线程在没有synchronized
块的情况下做一些事情:
foo.setBar(); // No synchronization first.
Run Code Online (Sandbox Code Playgroud)
由于运气不好(或墨菲定律,“任何可能出错的事情,都会出错。”),这两个线程可能碰巧同时执行。在一些广泛使用的集合(例如,结构修改的情况下ArrayList
,HashSet
,HashMap
等等),这可能会导致一个ConcurrentModificationException
。
很难完全避免这个问题:
您可以记录同步要求,例如插入“blah
修改此集合之前必须同步”或“bloo
首先获取锁定”,但这依赖于用户发现、阅读、理解和应用指令。
有javax.annotation.concurrent.GuardedBy
注释,它可以帮助以标准化的方式记录这一点;问题是你必须有一些方法来检查工具链中注释的正确使用。例如,您可能可以使用Google 的 errorprone 之类的东西,它可以在某些情况下进行检查,但它并不完美。
有关集合的简单的操作,您可以使用的Collections.synchronizedXXX
工厂方法,它包裹的集合,使每一个方法调用底层集合下同步第一,如该SynchronizedCollection.add
法:
@Override public boolean add(E e) {
synchronized (mutex) { return c.add(obj); }
}
Run Code Online (Sandbox Code Playgroud)
哪里mutex
是同步实例(通常是SynchronizedCollection
它自己),c
是包装的集合。
这种方法的两个注意事项是:
您必须小心,不能以任何其他方式访问包装的集合,因为这将允许非同步访问,这是原始问题。这通常是通过在构建时立即包装集合来实现的:
Collections.synchronizedList(new ArrayList<T>());
Run Code Online (Sandbox Code Playgroud)每个方法调用都会应用同步,因此如果您正在执行某些复合操作,例如
if (c.size() > 5) { c.add(new Frob()); }
Run Code Online (Sandbox Code Playgroud)
那么您在整个操作过程中没有独占访问权限,只能单独进行size()
和add(...)
调用。
为了在复合操作期间获得互斥访问,您需要进行外部同步,例如synchronized (c) { ... }
. 这需要您知道要同步的正确内容,但是,这可能是也可能不是c
。
归档时间: |
|
查看次数: |
23600 次 |
最近记录: |