问题
I have a class like the following:
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
may be modified concurrently: one is via remove()
by one thread and two via the shallow copied instance returned by getAllPersons()
.
I have tested the above scenario in a multithreaded environment to see if I can avoid ConcurrentModificationException
by returning a shallow copy when getAllPersons()
is called. It seemed to work. I have never once encountered a ConcurrentModificationException
.
Why, in this case, does making only a shallow copy of persons
avoid a ConcurrentModificationException
?
回答1:
A ConcurrentModificationException is thrown when a collection changes in a manner which invalidates open iterators. This usually happens when a collection which is not thread safe is accessed by multiple threads (although this is not the only cause)
There is still a small error in your code - to safely access a member which is not itself thread safe, you should synchronize
on the getAllPersons method.
Assuming that is fixed -- because you are returning a copy, the collection itself cannot be modified by other callers (each gets their own copy). That means that you can never get a ConcurrentModificationException.
Note that this does not protect you against thread safety issues with your Person
class, only the collections themselves. If Person
is immutable, you should be OK.
In this case, a better solution would be to directly use a CopyOnWriteArrayList which implements similar semantics, but only copies when you actually write to the list - not every time you read from it.
回答2:
That's because you are returning a copy of the List rather than the list itself. remove()
is the only method which is modifying the actual list, accessible by multiple threads. Threads calling the getAllPersons()
method will anyways getting a new list, so if they modify this list, its not going to change the original list.
So as your collection is not getting modified concurrently by threads you are not getting ConcurrentModificationException.
来源:https://stackoverflow.com/questions/7169913/avoiding-concurrentmodificationexception-on-list-by-making-a-shallow-copy