即使列表同步,为什么会出现ConcurrentModificationException?

时间:2014-08-04 07:53:35

标签: java concurrentmodification

我有Android多线程应用程序。

两个或多个触发器有可能运行相同的代码部分。

我有一个对象列表。

我通过Collections.synchronizedList

进行同步
private List<WmGroupItemSample> mGroupItemSampleList;

mGroupItemSampleList = new ArrayList<WmGroupItemSample>();
mGroupItemSampleList = Collections.synchronizedList(mGroupItemSampleList);

然而有时我在网上得到了例外:

Collections.sort(mGroupItemSampleList, new GroupItemSampleComparator());

java.util.ConcurrentModificationException
       at java.util.AbstractList$SimpleListIterator.next(AbstractList.java:62)
       at java.util.Collections.sort(Collections.java:1895)
  • 此流程合法吗?
  • 我是否需要创建副本并在副本上运行排序?
  • 为什么Collections.synchronizedList不会阻止此异常?

[编辑]

GroupItemSampleComparator

public class GroupItemSampleComparator implements java.util.Comparator<WmGroupItemSample> {

    public GroupItemSampleComparator() {
        super();        
    }

    public int compare(WmGroupItemSample s1, WmGroupItemSample s2) {
       return ( (s2.getStartDate() - s1.getStartDate()) > 0 ) ? (-1) : (1);
    }
}

谢谢,

4 个答案:

答案 0 :(得分:10)

基本问题是同步列表没有以有用的方式同步。

问题是,虽然它的方法是同步的,但是移动元素应该是原子的动作不是,因为移动所需的单独调用不会同步在一起。这意味着其他线程可以介入各个方法调用之间。因此,同步集合现在基本上已被弃用。

尽管有这个缺陷,如果你的线程在排序时有另一个线程添加一个元素,你将得到这个异常,因为sort迭代并在迭代期间更改列表会导致异常。

幸运的是,JDK具有新的Collection类,这些类具有工业强度(和有用)同步,由java.util.concurrent包提供。

将您的列表替换为CopyOnWriteArrayList,不要“同步”它,您就可以了。

答案 1 :(得分:4)

Collections.synchronizedList(list)返回一个同步列表,这意味着列表的方法将被同步(其中只有一个可以同时运行)。

然而 意味着你不能调用列表的方法,而其他人(或者你)正在使用迭代器迭代列表(iterator()返回的迭代器不同步)。 synchronizedList()如果有人在列表上进行迭代并且以迭代器的方法之外的任何其他方式进行修改,则ConcurrentModificationException不会保护您获得GroupItemSampleComparator

修改

同样,您的0不好,如果通过equals()方法认为传递的2个对象相等,则必须返回getStartDate()。试试这个(假设long返回public int compare(WmGroupItemSample s1, WmGroupItemSample s2) { long diff = s2.getStartDate() - s1.getStartDate(); return diff > 0 ? -1 : diff < 0 ? 1 : 0; } ):

{{1}}

答案 2 :(得分:2)

仅在多线程环境中不会发生此异常。例如,如果您在迭代期间迭代列表并删除元素,则可能发生此异常(取决于您删除该元素的方式)。

答案 3 :(得分:2)

也许这会有所帮助 - 没有看到所有代码,也没有其他访问列表的机会。在synchronizedList(List<T> list)

上引用Javadoc

返回由指定列表支持的同步(线程安全)列表。为了保证串行访问,必须通过返回的列表完成对后备列表的所有访问。

当迭代时,用户必须手动同步返回的列表:

List list = Collections.synchronizedList(new ArrayList());
  ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

那么,这个列表上的所有迭代都是以这种方式保护的吗?