How to prevent nested synchronized blocks when iterating over a collection

In a multi-threaded Java application, I need to iterate over a collection of objects. Since both the collection and objects can be modified by another thread, while I repeat them, I need to use synchronization.

However, nested synchronized blocks are not recommended because they can lead to deadlocks. How can I solve this problem?

Collection<Data> dataCollection = something.getDataCollection(); synchronized ( dataCollection ) { for ( final Data data : dataCollection ) { synchronized ( data ) { data.doSomething(); // doSomething() changes object state } } } 
+6
java multithreading synchronization nested
source share
6 answers

I think you can use CopyOnWriteArrayList instead of external synchronization.

A threaded version of ArrayList, in which all mutative operations (add, set, etc.) are implemented by creating a new copy of the base array. This is usually too expensive, but can be more effective than alternatives when bypass operations far exceed the number of mutations and is useful when you cannot or do not want to synchronize bypasses, but should eliminate interference between parallel threads.

+4
source share

You can take a copy of the collection and lock only 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 } } 
+5
source share

Nested synchronization can lead to deadlock, but this is not necessary. One way to avoid deadlocks is to determine the order in which you synchronize objects and always follow it.

If you always synchronize a dataCollection object before synchronizing data objects, you will not lock.

+3
source share

I can’t believe that no one pointed out that method number one, to avoid synchronization on the Data object, is to make this object itself thread safe! This is also the right way to handle synchronization - if you know that multiple threads will be available to your object, handle the synchronization as you see fit inside the class, and not in the code that can access it. You will also certainly be more efficient, because you can limit synchronization to critical blocks only, use ReadWriteLock, jucatomic, etc.

+3
source share

Take a look at ReentrantReadWriteLock . With this class, you can implement a lock that allows any number of threads without changing (reading) to access the shared property at the same time, but only one changing (writing) stream to access it at a time (all other readers and writers are blocked until until the recording stream releases the write lock). Remember to thoroughly test your implementation, as improper use of locks can still lead to race conditions and / or deadlocks.

+1
source share

Whether you use CopyOnWriteArrayList, as Bojo said, or copy the list before repeating, as Peter says, should depend on how much you expect the list to be edited compared to repeating.

Use CopyOnWriteArrayList when you expect the list to be repeated much more than it was modified.

Use a copy of the list if you think that it will be changed much more than it is renamed.

These should be the first options, because concurrency solutions should be simple, if it is not inevitable, but if none of these situations apply, you will need to choose one of the more complex strategies described in the comments here.

Good luck

+1
source share

All Articles