Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need to manually synchronize the Synchronized list while iteration when it could be avoided?

My question is about synchronizedList method Collections Class.

Javadocs say:

It is imperative that the user manually synchronize on the returned list when iterating over it:

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

Though manually synchroniziation is not required for other methods. I looked into the source code of Collections class and found shyncronization has already been taken care for all methods like add

public boolean add(E e) {
   synchronized(list) {return c.add(e);}
}

but not for iterator method. I think iterator method could have also handled synchronization in the same fashion as above method (it would have avoided the extra work i.e manual synchronization for programmers). i am sure there must be some concrete reason behind it but i am missing it?

public Iterator<E> iterator() {
   return c.iterator(); // Must be manually synched by user!
}

A way to avoid manual synchronization from Programmer

public Iterator<E> iterator() {
   synchronized(list) {
       return c.iterator(); // No need to manually synched by user!
   }
}
like image 837
M Sach Avatar asked Sep 18 '13 13:09

M Sach


2 Answers

I think iterator method could have also handled synchronization in the same fashion as above method

No, it absolutely couldn't.

The iterator has no control over what your code does between calls to the individual methods on it. That's the point. Your iteration code will call hasNext() and next() repeatedly, and synchronization during those calls is feasible but irrelevant - what's important is that no other code tries to modify the list across the whole time you're iterating.

So imagine a timeline of:

t = 0: call iterator()
t = 1: call hasNext()
t = 2: call next()
// Do lots of work with the returned item
t = 10: call hasNext()

The iterator can't synchronize between the end of the call to next() at t=2 and the call to hasNext() at t=10. So if another thread tries to (say) add an item to the list at t=7, how is the iterator meant to stop it from doing so?

This is the overall problem with synchronized collections: each individual operation is synchronized, whereas typically you want a whole chunky operation to be synchronized.

like image 133
Jon Skeet Avatar answered Sep 23 '22 14:09

Jon Skeet


If you don't synchronize the entire iteration, another thread could modify the collection as you iterate, leading to a ConccurentModificationException.

Also, the returned iterator is not thread-safe.
They could fix that by wrapping the iterator in a SynchronizedIterator that locks every method in the iterator, but that wouldn't help either – another thread could still modify the collection between two iterations, and break everything.

This is one of the reasons that the Collections.synchronized*() methods are completely useless.
For more information about proper thread-safe collection usage, see my blog.

like image 4
SLaks Avatar answered Sep 23 '22 14:09

SLaks