简体   繁体   中英

Thread-safety simple

If I have a list of Components in a multithreading environnment and if I do any operation on this list except add (I use the keyword synchronized on the list in this case) and get (the method called by a component is thread-safe), is that thread-safe?

public class Test {

   private final ArrayList<Component> myContainer = new ArrayList<Component>();

   public void add(Component){
      synchronized(myContainer){
          myContainer.add(Component)
      }
   }

   public void useComponents()
   {
      for(Component c : myContainer)
           c.use(); // method thread-safe
   }

   // no other operations on myContainer
}

In the current form, it is not thread-safe: The useComponents method could be executed by one thread. At the same time, another thread could call add , and thus modify the collection while it is being iterated over. (This modification could happen between two calls to c.use() , so the fact that the use() method is thread safe would not help you here).

Strictly speaking, this is not even restricted to multithreading : If the c.use() internally called test.add(someOtherComponent) (even if it was done in the same thread!) this would throw a ConcurrentModificiationException , because again, the collection is modified while being iterated over.

Thread safety (without safety agains concurrent modifications) could be achieved by simply wrapping the iteration into a synchronized block:

public void useComponents()
{
   synchronized (myContainer) 
   {
       for(Component c : myContainer)
           c.use(); // method thread-safe
   }

}

However, this would still leave the possibility of a ConcurrentModificationException . Most likely the c.use() call will not (and should not) modify the collection that the component is contained in (otherwise, one could question the design in general).

If you wanted to allow the c.use() call to modify the collection, you could replace the collection with a CopyOnWriteArrayList :

private final List<Component> myContainer = 
    new CopyOnWriteArrayList<Component>();

and then you could even remove the synchroniziation completely. But you should be aware of the implications: The contents of the list will be copied during each modification (hence the name...). This is usually used in cases where you have a small collection that is frequently iterated over, but which is rarely modified. (Listeners in all forms are a classic example here).

It looks ok, except that I am not sure about iterator behavior in useComponents() if you'll simultaneously add elements to the list.

Did you consider using CopyOnWriteArrayList instead?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM