2011-08-24 127 views
4

避免对名单ConcurrentModificationException的我有类似下面的类:通过使浅拷贝

class Test 
{ 
    private LinkedList<Person> persons = new LinkedList<Person>; 

    public synchronized void remove(Person person) 
    { 
     persons.remove(person); 
    } 

    public List<Person> getAllPersons() 
    { 
     // Clients may iterate over the copy returned and modify the structure. 
     return new ArrayList<Person>(persons); 
    } 
} 

persons可以同时修改:通过由getAllPersons()返回的浅复制的实例是通过remove()由一个线程和两个。

我已经在多线程环境中测试了上述场景,以便在调用getAllPersons()时通过返回浅拷贝来避免ConcurrentModificationException。它似乎工作。我从未遇到过ConcurrentModificationException

为什么在这种情况下,只做persons的浅拷贝避免ConcurrentModificationException

+0

http://stackoverflow.com/questions/19384056/how-to-concurrently-modify-a-vector/19384832?noredirect=1#comment28729008_19384832 –

回答

6

集合更改的方式会导致打开迭代器失效时引发ConcurrentModificationException。这通常发生在多线程访问非线程安全的集合时(尽管这不是唯一原因)

代码中仍然存在一个小错误 - 安全地访问不是线程安全的成员,您应该在getAllPersons方法上使用synchronize

假设这是固定的 - 因为您要返回副本,集合本身不能被其他调用者修改(每个获得自己的副本)。这意味着你永远不会得到ConcurrentModificationException。

请注意,这个不是保护您免受您的Person类的线程安全问题,只有收集本身。如果Person是不可变的,你应该没问题。

在这种情况下,更好的解决方案是直接使用CopyOnWriteArrayList,它实现类似的语义,但只有在实际写入列表时才会复制 - 而不是每次读取它时。

+1

除了他正在使用CopyOnReadArrayList! – Bringer128

+0

编辑,使之更清晰 –

1

这是因为您要返回列表的副本而不是列表本身。 remove()是修改实际列表的唯一方法,可由多个线程访问。调用getAllPersons()方法的线程无论如何都会得到一个新的列表,所以如果他们修改这个列表,它不会改变原始列表。 因此,如果线程没有同时修改您的集合,那么您没有收到ConcurrentModificationException。