5

我有一个像下面这样的类:

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可以同时修改:一个是通过remove()一个线程,两个是通过getAllPersons().

我已经在多线程环境中测试了上述场景,看看是否可以通过在调用ConcurrentModificationException时返回浅拷贝来避免。getAllPersons()它似乎奏效了。我从来没有遇到过ConcurrentModificationException.

为什么,在这种情况下,只制作persons避免 a的浅表副本ConcurrentModificationException

4

2 回答 2

6

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

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

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

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

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

于 2011-08-24T02:54:06.813 回答
1

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

于 2011-08-24T05:01:16.737 回答