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
}
}
}
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.
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.
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.
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.
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
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With