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());
    }
}