java - 线程安全的 CopyOnWriteArrayList 反向迭代

标签 java collections

考虑以下代码片段:

private List<Listener<E>> listenerList = new CopyOnWriteArrayList<Listener<E>>();

public void addListener(Listener<E> listener) {
    if (listener != null) {
        listenerList.add(listener);
    }
}

public void removeListener(Listener<E> listener) {
    if (listener != null) {
        listenerList.remove(listener);
    }
}

protected final void fireChangedForward(Event<E> event) {
    for (Listener<E> listener : listenerList) {
        listener.changed(event);
    }
}

protected final void fireChangedReversed(Event<E> event) {
    final ListIterator<Listener<E>> li = listenerList.listIterator(listenerList.size());
    while (li.hasPrevious()) {
        li.previous().changed(event);
    }
}

有一个可以修改和迭代的监听器列表。 我认为前向迭代(参见方法#fireChangedForward) 应该是安全的。 问题是:反向迭代(参见方法#fireChangedReversed)在多线程环境中是否也安全? 我对此表示怀疑,因为涉及两个调用:#size 和#listIterator。 如果它不是线程安全的,那么在以下情况下实现#fireChangedReversed 的最有效方法是什么:

  • 优化遍历
  • 尽可能避免使用锁定
  • 避免使用 javax.swing.event.EventListenerList
  • 首选不使用第三方库的解决方案,例如可以用自己的代码实现

最佳答案

确实,listenerList.listIterator(listenerList.size()) 不是线程安全的,这正是您建议的原因:列表可能会在调用 size() 之间更改大小listIterator(),导致从迭代中遗漏一个元素,或者抛出 IndexOutOfBoundsException

处理此问题的最佳方法是在获取迭代器之前克隆 CopyOnWriteArrayList:

    CopyOnWriteArrayList<Listener<E>> listenerList = ... ;
    @SuppressWarnings("unchecked")
    List<Listener<E>> copy = (List<Listener<E>>)listenerList.clone();
    ListIterator<Listener<E>> li = copy.listIterator(copy.size());

克隆对列表进行浅拷贝。特别是,克隆与原始数组共享内部数组。这在规范中并不完全明显,它只是说

Returns a shallow copy of this list. (The elements themselves are not copied.)

(当我读到这里时,我想“当然没有复制元素;这是一个浅拷贝!”这真正的意思是元素和包含它们的数组都没有被复制。)

这相当不方便,包括缺少 clone() 的协变覆盖,需要未经检查的转换。

JDK-6821196 中讨论了一些潜在的增强功能和 JDK-8149509 .前一个错误还链接到 discussion这个问题在并发兴趣邮件列表上。

关于java - 线程安全的 CopyOnWriteArrayList 反向迭代,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42045206/

相关文章:

java - 绘制圆圈内刻的字符串

javascript - 如何测试 JavaScript 对象是否是有序类型的对象

java - 不使用迭代器过滤列表

java - 两个 Set 包含相同的元素,但它们不相等。为什么是这样的结果?

java - 替代 BeanUtils.getProperty()

java - 如何阻塞直到 BlockingQueue 为空?

java - 是否可以在一个类中同时实现 scala.collection.Seq[T] 和 java.util.List[T]

.net - List、IList、IEnumerable、IQueryable、ICollection,哪个返回类型最灵活?

java - 从数组列表中获取数组项

java - 如何从 SQLException 对象获取 SQLException 原因