我有2个Main类的内部线程类。有时,当一个添加新元素而另一个元素被删除时,它会导致ConcurrentModificationException。我想我不知道如何同步它们。
NSURLCache
答案 0 :(得分:2)
ConcurrentModificationException是由ThreadA中的set.add(obj)引起的,而迭代在ThreadB中进行(而不是在循环期间由set.remove())。
需要同步线程以避免这种情况。
使用某些对象上的内部锁来同步线程。您使用' synchronized'来声明这一点。关键字:
// entire method synchronized on 'this'
synchronized SomeValue foo();
// block synchronized on obj:
synchronized( obj ) {
// stuff.
}
根据您需要同步的内容,细节会有很大差异。对于集合,通常可以安全地隔离特定操作,如add()或remove(),但如果需要迭代集合中的元素,则需要同步整个块,如果您进行迭代使用常规集合实现:
synchronized( set ) {
for (Iterator<MyObject> i = set.iterator(); i.hasNext();) {
...
}
}
但是,通常可以根据集合的性质实现更有效的同步,并且在手动同步时很容易出错。对于大多数情况,通常最好只使用java.util.concurrent中的一个集合实现,它实现了线程安全的迭代器,并且不会从其他线程的操作中抛出ConcurrentModificationException。
由于某种原因,没有Set的ConcurrentHashSet实现,但是可以使用newSetFromMap获取一个实例:
HashSet<MyObject> set = Collections.newSetFromMap( new ConcurrentHashMap<MyObject,Object>() );
答案 1 :(得分:1)
使用集合副本删除项目,迭代集合时无法删除项目。
要同步,请使用Lock()
集合。
Lock myLock= new Lock();
myLock.lock();
set.add(item);
myLock.unlock();
myLock.lock();
...while loop and modification..
myLock.unlock();
答案 2 :(得分:1)
最简单的解决方案是将读取代码与写入代码隔离开来。您可以通过使用synchronized(set)
块包围修改来实现此目的。对于第一个呼叫,我们必须围绕添加呼叫进行同步:
run(running){
...
synchronized(set) {
set.add(obj);
}
...
}
对于第二次调用,我们需要在整个迭代期间进行同步以避免并发修改。 i.remove()
在单线程情况下是正确的,但正如您所发现的,它在多个线程中不起作用。
synchronized(set) {
for (Iterator<MyObject> i = set.iterator(); i.hasNext();) {
MyObject obj= i.next();
if (!obj.isSmt()) {
i.remove();
...
}
}
}
synchronized(set)
是对象set
的锁定。只有一个线程能够在给定时间输入任一个同步块,防止在线程迭代时将项目添加到集合中。
答案 3 :(得分:0)
在迭代元素时,您无法修改元素列表。这将导致ConcurrentModificationException
。如果要删除元素,请将其存储在临时列表中,然后在迭代完成后删除列表。