当另一个线程正在使用 fireEvent() 中的迭代器时,为什么在以下代码中调用 removeListener() 会抛出 ConcurrentModificationException

public class MyClass { 
 
    private Set<Object> synchronizedListeners; 
 
    public MyClass() { 
        synchronizedListeners = Collections.synchronizedSet( 
                new LinkedHashSet<Object>()); 
    } 
 
    public void addListener(Object listener) { 
        synchronizedListeners.add(listener); 
    } 
 
    public synchronized void removeListener(Object listener) { 
        synchronizedListeners.remove(listener); 
    } 
 
    public void fireEvent() { 
        synchronized (synchronizedListeners) { 
            for (Object listener : synchronizedListeners) { 
                // do something with listener 
            } 
        } 
    } 
} 

据我了解,由于我在 fireEvent() 中使用了 synchronized (synchronizedListeners),这应该会阻止任何其他调用 removeListener()< 的线程,直到 fireEvent() 中的迭代完成,此时从该 Set 中删除元素应该是安全的。但事实似乎并非如此。我做错了什么?

可能相关:Java synchronized block vs. Collections.synchronizedMap

编辑:有人指出我不必要地同步了 removeListener() 方法。所以我尝试了这个版本:

public void removeListener(Object listener) { 
    synchronizedListeners.remove(listener); 
} 

还是报同样的错

编辑 2: 正如 assylias 指出的那样,问题在上面的代码中不可见。我在导致错误的 synchronized (synchronizedListeners) block 中的 for 循环内部调用 removeListener()。在这种情况下,我最终使用的修复方法是从另一个线程中删除监听器:

public void removeListener(final Object listener) { 
    new Thread() { 
        @Override 
        public void run() { 
            synchronizedListeners.remove(listener); 
        } 
    }.start(); 
} 

请您参考如下方法:

您正在同步两个不同的对象。

removeListener 方法在 MyClass 实例上同步,而 fireEvent 中的循环在 synchronizedListeners 上同步> 设置。

您需要做的是在集合本身上同步每个使用 synchronizedListeners 的方法。


评论关闭
IT干货网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!