Java ConcurrentModificationException尽管使用了synchronized
尽管声明头中有synchronized语句,但在使用iterator.next()的行中仍然会出现ConcurrentModificationException异常;这里怎么了Java ConcurrentModificationException尽管使用了synchronized,java,concurrency,iterator,Java,Concurrency,Iterator,尽管声明头中有synchronized语句,但在使用iterator.next()的行中仍然会出现ConcurrentModificationException异常;这里怎么了 ConcurrentModificationException通常与多线程无关。大多数情况下,它发生是因为您正在修改它在迭代循环体中迭代的集合。例如,这将导致它: public synchronized X getAnotherX(){ if(iterator.hasNext()){ X b = itera
ConcurrentModificationException
通常与多线程无关。大多数情况下,它发生是因为您正在修改它在迭代循环体中迭代的集合。例如,这将导致它:
public synchronized X getAnotherX(){
if(iterator.hasNext()){
X b = iterator.next();
String name = b.getInputFileName();
...
return b;
}
else{return null;}
}
在这种情况下,必须使用迭代器.remove()方法。如果要添加到集合中,同样会发生这种情况,在这种情况下,没有通用解决方案。但是,如果处理列表,可以使用子类型
ListIterator
,它有一个add()
方法。我同意上面关于ConcurrentModificationException
的陈述,这通常是由于在与迭代相同的线程中修改集合而发生的。然而,这并不总是原因
关于synchronized
需要记住的是,只有当访问共享资源的每个人都同步时,它才保证独占访问
例如,您可以同步对共享变量的访问:
Iterator iterator = collection.iterator();
while (iterator.hasNext()) {
Item item = (Item) iterator.next();
if (item.satisfiesCondition()) {
collection.remove(item);
}
}
你可以认为你有独家访问权。但是,如果没有synchronized
块,就无法阻止另一个线程执行某些操作:
synchronized (foo) {
foo.setBar();
}
由于运气不好(或者“任何可能出错的事情都会出错”),这两个线程可能会同时执行。在对一些广泛使用的集合进行结构修改的情况下(例如,ArrayList
,HashSet
,HashMap
等),这可能会导致ConcurrentModificationException
很难完全防止这个问题:
- 您可以记录同步要求,例如插入“您必须在修改此集合之前在
上同步”或“获取blah
lock first”,但这取决于用户发现、阅读、理解和应用说明 有bloo
注释,它可以帮助以标准化的方式记录这一点;问题是,您必须有一些方法来检查注释在工具链中的正确使用。例如,您可能可以使用类似的东西,它可以在某些情况下进行检查,但是javax.annotation.concurrent.GuardedBy
- 对于集合上的简单操作,可以使用工厂方法,这些方法包装集合,以便每个方法调用首先在基础集合上同步,例如:
其中,
是synchronized on实例(通常是mutex
本身),而SynchronizedCollection
是包装的集合 这种方法的两个注意事项是:c
- 您必须小心,不能以任何其他方式访问包装的集合,因为这将允许非同步访问,这是最初的问题。这通常是通过在构建时立即包装集合来实现的:
然后,在整个操作过程中,您没有独占访问权,仅对@Override public boolean add(E e) { synchronized (mutex) { return c.add(obj); } }
和size()
调用单独进行访问 为了在复合操作期间获得互斥访问,您需要进行外部同步,例如add(…)
。但是,这要求您知道要同步的正确对象,它可能是synchronized(c){…}
c
if (c.size() > 5) { c.add(new Frob()); }
公共类SynchronizedListDemo{
公共静态void main(字符串[]args)引发InterruptedException{
列表=新的ArrayList();
对于(int i=0;ii)我不明白,我只是想要一个字符串(在本例中是在名为“b”的对象中)。但我尝试使用迭代器.remove();但没有帮助。同样的异常也会出现。没有问题:公共同步块getAnotherBlock(){Block b=null;if(iterator.hasNext()){b=iterator.next();iterator.remove();}String name=b.getInputFileName();Integer[]arr=blocksperfileft.get(name);arr[1]+=1;blocksperfileft.put(b.getInputFileName(),arr);currBlockPos++;//incr global var return b;好的,这很好,但在本例中,在取出迭代器(即调用
迭代器()后,对列表进行任何修改)
方法)即使对集合的每次访问都是同步的,也将导致ConcurrentModificationException
。您不能将对迭代器方法的调用与集合的突变交织在一起。要了解原因,请考虑迭代器是如何实现的,以及如果有人在集合之前或之后插入或删除元素,会发生什么情况在迭代器的当前位置之后。+1在语句中:ConcurrentModificationException通常与多个线程无关。大多数情况下,它发生是因为您正在修改它在迭代循环体中迭代的集合。
。这是我在遇到此错误我确实认为它主要是由于多线程造成的。如果一个线程正在修改,而另一个线程已经在迭代。如果可以避免使用迭代器,那就太好了。迭代器就像在迭代器创建时创建快照,然后继续检查集合是否已修改。请参阅可能的重复
Collections.synchronizedList(new ArrayList<T>());
if (c.size() > 5) { c.add(new Frob()); }
public class SynchronizedListDemo {
public static void main(String[] args) throws InterruptedException {
List<Integer> list = new ArrayList<>();
for(int i=0;i<100000;i++){
System.out.println(i);
list.add(i);
}
// Synchronzied list will also give ConcurrentModificationException, b'coz
// it makes only methods thread safe, but you are still modifying list while iterating it.
// You can use 'ListIterator' or 'CopyOnWriteArrayList'
List<Integer> list1 = Collections.synchronizedList(list);
Runnable r1= ()->{
for(Integer i: list1)
System.out.println(i);
};
Runnable r2 = ()->{
try {
System.out.println();
System.out.println("Removing....");
//list1.add(4); // Will give ConcurrentModificationException
System.out.println("Removed");
} catch (Exception e) {
e.printStackTrace();
}
};
// This will not give ConcurrentModificationException as it work on the copy of list.
List<Integer> list2 = new CopyOnWriteArrayList<>(list);
Runnable r3= ()->{
for(Integer i: list2)
System.out.println(i);
};
Runnable r4 = ()->{
try {
System.out.println();
System.out.println("Removing....");
list2.add(4);
System.out.println("Removed");
} catch (Exception e) {
e.printStackTrace();
}
};
Thread t1 = new Thread(r3);
Thread.sleep(100);
Thread t2 = new Thread(r4);
t1.start();
t2.start();
System.out.println("Done");
}