Songo
Songo

Reputation: 5736

Why do we still need external synchronization when a synchronizedList() or Vector is already synchronized?

ArrayList is not synchronized. But there's a way the get a synchronized one as mentioned in java.util.ArrayList's JavaDoc:

List list = Collections.synchronizedList(new ArrayList(...));

In java.util.Collections JavaDoc you can read that "It is imperative that the user manually synchronize on the returned list when iterating over it:"

synchronized(list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
    foo(i.next());
}

Using a synchronized ArrayList results in additional work, why not use a java.util.Vector instead? Is there an advantage in using this approach?

I found this question on http://www.coderanch.com/ and I'm sharing its great answer here from the same post.

Upvotes: 3

Views: 935

Answers (1)

Gray
Gray

Reputation: 116908

In java.util.Collections JavaDoc you can read that "It is imperative that the user manually synchronize on the returned list when iterating over it:"

If you are doing multiple transactions on a list (like iterating across it), then a synchronized list will not protect you from a ConcurrentModificationException. There is still the possibility that someone will add or remove to the list while you are iterating across it and that will throw.

The Collections.synchronizedList() protects the list against concurrent operations from multiple threads. In the case of the iterator, you are making list calls but then returning to your code and then making list calls again. Even though the methods are synchronized, you are not protected against the race conditions of other threads operating on the list in the meantime.

Using a synchronized ArrayList results in additional work, why not use a java.util.Vector instead? Is there an advantage in using this approach?

Vector is not going to help here. It will also throw an exception under the same circumstance. To quote from the javadocs:

The Iterators returned by Vector's iterator and listIterator methods are fail-fast: if the Vector is structurally modified at any time after the Iterator is created, in any way except through the Iterator's own remove or add methods, the Iterator will throw a ConcurrentModificationException.

Also, Vector is a very old class. It is widely held that it is better to use a synchronized ArrayList.

Here's a couple of things that you can do:

  • You can copy the list into another collection which you then can iterate across. Even if other threads add or delete from the synchronized list, your local collection is safe. This doesn't protect you from modifications to the elements of the list of course.
  • You could switch to a fully concurrent collection such as ConcurrentHashMap (it's not a list of course), that handles modifications to the collection while you are iterating. ConcurrentSkipList or LinkedBlockingQueue are other choices.
  • As @Geek points out, CopyOnWriteArrayList is another option although the performance depends a lot on the ratio to iterating and writing.

Upvotes: 8

Related Questions