Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent nested synchronized blocks when iterating over a collection

In a multithreaded Java application I need to iterate over a collection of objects. Since both the collection and the objects could be modified by another thread while I iterate over them, I need to use synchronization.

However nested synchronized blocks are not recommended since they could lead to deadlocks. How would I solve this problem?

Collection<Data> dataCollection = something.getDataCollection();

synchronized ( dataCollection ) {
  for ( final Data data : dataCollection ) {
    synchronized ( data ) {
      data.doSomething();  // doSomething() changes object state
    }
  }
}
like image 882
Sven Jacobs Avatar asked Feb 26 '11 18:02

Sven Jacobs


People also ask

What is nested monitor lockout?

Nested-monitor lockoutThis problem usually occurs when you call a blocking function from within a synchronized method, and the only way to release the block is to call another synchronized method.

Why locks are better than synchronized?

Only one thread is allowed to access only one method at any given point of time using a synchronized block. This is a very expensive operation. Locks avoid this by allowing the configuration of various locks for different purpose.

What is the advantage of synchronized block over synchronized method?

Synchronized blocks provide granular control over a lock, as you can use arbitrary any lock to provide mutual exclusion to critical section code. On the other hand, the synchronized method always locks either on the current object represented by this keyword or class level lock, if it's a static synchronized method.

Can synchronized cause deadlock?

Unfortunately, using synchronization can itself cause problems. Thread synchronization involves acquiring an exclusive lock. Only the one thread that currently holds the lock can execute the synchronized code. When a program uses more than one lock, however, a situation known as deadlock can arise.


2 Answers

I think you can use CopyOnWriteArrayList instead of the outer synchronization.

A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array. This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads

like image 77
Bozho Avatar answered Oct 01 '22 06:10

Bozho


You can take a copy of the collection and only lock one object at a time.

Collection<Data> dataCollection = something.getDataCollection();
Collection<Data> copy;
synchronized ( dataCollection ) {
  copy = new ArrayList<Data>(dataCollection);
}

for (Data data : copy) {
    synchronized ( data ) {
      data.doSomething();  // doSomething() changes object state
    }
}
like image 45
Peter Lawrey Avatar answered Oct 01 '22 04:10

Peter Lawrey