Java Android:当另一个线程可以从集合中删除元素时,如何正确地在集合上同步迭代?
当另一个线程正在使用Java Android:当另一个线程可以从集合中删除元素时,如何正确地在集合上同步迭代?,java,android,multithreading,synchronization,Java,Android,Multithreading,Synchronization,当另一个线程正在使用firevent()中的迭代器时,为什么在下面的代码中调用removeListener()会引发ConcurrentModificationException 但还是犯了同样的错误 编辑2:正如assylias指出的那样,问题在上述代码中不可见。我从导致错误的synchronized(synchronizedListeners)块中的for循环内部调用了removeListener()。在本例中,我最终使用的修复方法是从另一个线程中删除侦听器: public void rem
firevent()
中的迭代器时,为什么在下面的代码中调用removeListener()
会引发ConcurrentModificationException
但还是犯了同样的错误
编辑2:正如assylias指出的那样,问题在上述代码中不可见。我从导致错误的synchronized(synchronizedListeners)
块中的for循环内部调用了removeListener()
。在本例中,我最终使用的修复方法是从另一个线程中删除侦听器:
public void removeListener(final Object listener) {
new Thread() {
@Override
public void run() {
synchronizedListeners.remove(listener);
}
}.start();
}
您正在两个不同的对象上同步
RemovelListener
方法在MyClass
实例上同步,而fireEvent
中的循环在synchronizedListeners
集合上同步
您需要做的是在集合本身上使用每一个使用<代码>同步的侦听器< /代码>的方法。
我不能复制您所描述的——底部的代码给出下面的输出——这表明在迭代的中间调用了删除。但直到迭代之后才完成,因为您使用的是同步的集合。这是人们所期望的行为,不会抛出ConcurrentModificationException。请注意,我已经从removeListener
方法中删除了synchronized
关键字,因为它在这里没有用处
fire 100000
remove 100000
done fire 100000
done remove 99999
结论:问题出在其他地方。例如,如果您有一个子类覆盖了fireEvent方法,或者如果您构建的同步集与发布的代码不完全相同
public static void main(String[] args) {
final MyClass mc = new MyClass();
final Object o = new Object();
mc.addListener(o);
for (int i = 0; i < 99999; i++) {
Object o1 = new Object();
mc.addListener(o1);
}
Runnable remove = new Runnable() {
@Override
public void run() {
mc.removeListener(o);
}
};
new Thread(remove).start();
mc.fireEvent();
}
public static class MyClass {
protected Set<Object> synchronizedListeners = Collections.synchronizedSet(new LinkedHashSet<Object>());
public void addListener(Object listener) {
synchronizedListeners.add(listener);
}
public void removeListener(Object listener) {
System.out.println("remove " + synchronizedListeners.size());
synchronizedListeners.remove(listener);
System.out.println("done remove " + synchronizedListeners.size());
}
public void fireEvent() {
System.out.println("fire " + synchronizedListeners.size());
synchronized (synchronizedListeners) {
for (Object listener : synchronizedListeners) {
// do something with listener
}
}
System.out.println("done fire " + synchronizedListeners.size());
}
}
publicstaticvoidmain(字符串[]args){
最终MyClass mc=新MyClass();
最终对象o=新对象();
mc.addListener(o);
对于(int i=0;i<99999;i++){
对象o1=新对象();
mc.addListener(o1);
}
Runnable remove=new Runnable(){
@凌驾
公开募捐{
mc.重组人(o);
}
};
新螺纹(移除)。开始();
mc.firevent();
}
公共静态类MyClass{
受保护集synchronizedListeners=Collections.synchronizedSet(新LinkedHashSet());
公共void addListener(对象侦听器){
synchronizedListeners.add(侦听器);
}
公共void RemovelListener(对象侦听器){
System.out.println(“remove”+synchronizedListeners.size());
同步侦听器。删除(侦听器);
System.out.println(“完成删除”+synchronizedListeners.size());
}
公共事件(){
System.out.println(“fire”+synchronizedListeners.size());
已同步(synchronizedListeners){
for(对象侦听器:synchronizedListeners){
//对听众做点什么
}
}
System.out.println(“完成开火”+synchronizedListeners.size());
}
}
synchronizedListeners
受保护-您有MyClass的子类吗?他们是否覆盖了任何方法?没有。我进行了编辑并将其更改为private以使其更清晰。您的代码不能抛出ConcurrentModificationException
。因此,问题并没有出现在您的问题中:您可能在发布之前过于简化了代码,并删除了问题的根源。我建议您尝试发布一个演示您的问题的帖子。您不要在fireEvent
循环中调用set.remove()
或removeListener()
,是吗?是的,我是从fireEvent
内部调用removeListener()
!它在一些调用中隐藏了几层,所以我错过了它。synchronizedListeners
是一个同步的集合:remove方法已经同步。removeListener
方法不需要同步,但在集合上添加同步不会产生任何影响…它会。如果两个块(remove()和循环)在同一个锁上同步,它们将永远不会为同一个对象并发执行。关键部分就是这样工作的。remove已经在内部同步:synchronizedListeners=Collections.synchronizedSet(newLinkedHashSet())
但是从SynchronizedSet创建的迭代器不是SynchronizedSet
继承了SynchronizedCollection
和iterator
方法的实现。你需要自己同步它们。@MartinMajer:你和我犯了同样的错误。内部锁(mutex
变量)在构造函数中设置为this
。请参阅我提供的链接。
fire 100000
remove 100000
done fire 100000
done remove 99999
public static void main(String[] args) {
final MyClass mc = new MyClass();
final Object o = new Object();
mc.addListener(o);
for (int i = 0; i < 99999; i++) {
Object o1 = new Object();
mc.addListener(o1);
}
Runnable remove = new Runnable() {
@Override
public void run() {
mc.removeListener(o);
}
};
new Thread(remove).start();
mc.fireEvent();
}
public static class MyClass {
protected Set<Object> synchronizedListeners = Collections.synchronizedSet(new LinkedHashSet<Object>());
public void addListener(Object listener) {
synchronizedListeners.add(listener);
}
public void removeListener(Object listener) {
System.out.println("remove " + synchronizedListeners.size());
synchronizedListeners.remove(listener);
System.out.println("done remove " + synchronizedListeners.size());
}
public void fireEvent() {
System.out.println("fire " + synchronizedListeners.size());
synchronized (synchronizedListeners) {
for (Object listener : synchronizedListeners) {
// do something with listener
}
}
System.out.println("done fire " + synchronizedListeners.size());
}
}