Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Avoiding ConcurrentModificationException on List by making a shallow copy

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?

like image 389
His Avatar asked Aug 24 '11 02:08

His


2 Answers

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.

like image 79
Steven Schlansker Avatar answered Oct 20 '22 01:10

Steven Schlansker


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.

like image 24
Swagatika Avatar answered Oct 20 '22 00:10

Swagatika