Java 通过创建浅拷贝避免列表上的ConcurrentModificationException

Java 通过创建浅拷贝避免列表上的ConcurrentModificationException,java,list,shallow-copy,concurrentmodification,Java,List,Shallow Copy,Concurrentmodification,我的课程如下: class Test { private LinkedList<Person> persons = new LinkedList<Person>; public synchronized void remove(Person person) { persons.remove(person); } public List<Person> getAllPersons() {

我的课程如下:

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);
    }
}
类测试
{
私人LinkedList人员=新LinkedList;
公共同步作废删除(人)
{
人。移除(人);
}
公共列表getAllPersons()
{
//客户端可以迭代返回的副本并修改结构。
返回新的ArrayList(人);
}
}
persons
可以同时修改:一个线程通过
remove()
执行,两个线程通过
getAllPersons()返回的浅复制实例执行

我已经在多线程环境中测试了上述场景,以查看在调用
getAllPersons()
时是否可以通过返回浅层副本来避免
ConcurrentModificationException
。这似乎奏效了。我从未遇到过
ConcurrentModificationException


在这种情况下,为什么只创建
人员的浅层副本可以避免
ConcurrentModificationException

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

代码中仍然有一个小错误-要安全地访问本身不是线程安全的成员,您应该在getAllPersons方法上进行
同步

假设这是固定的——因为您要返回一个副本,所以集合本身不能被其他调用方修改(每个调用方都有自己的副本)。这意味着您永远无法获得ConcurrentModificationException

请注意,此不会保护您的
Person
类的线程安全问题,仅保护集合本身。如果
Person
是不可变的,您应该可以


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

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

除非他使用的是CopyOnReadArrayList!