Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: need to synchronize on the widgetList while iterating the synchronized collection.

...

This noncompliant code example (based on Sun Developer Network SDN 2011 bug report 6687277) uses the ArrayList's remove() method to remove an element from an ArrayList while iterating over the ArrayList. The resulting behavior is unspecified.

Code Block
bgColor#FFcccc

class BadIterate {
  public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("one");
    list.add("two");
        
    Iterator iter = list.iterator();
    while (iter.hasNext()) {
      String s = (String)iter.next();
      if (s.equals("one")) {
        list.remove(s);
      }
    }
  }    
}

...

The Iterator.remove() method removes the last element returned by the iterator from the underlying Collection. Its behavior is fully specified, so it may be safely invoked while iterating over a collection.

Code Block
bgColor#ccccff

// ...
if (s.equals("one")) {
  iter.remove();
}
// ...

...

Although acceptable in a single-threaded environment, this noncompliant code example is insecure in a multithreaded environment because it is possible for another thread to modify the widgetList while the current thread iterates over the widgetList. Additionally, the doSomething() method could modify the collection during iteration.

Code Block
bgColor#FFcccc

List<Widget> widgetList = new ArrayList<Widget>();

public void widgetOperation() {
  // May throw ConcurrentModificationException
  for (Widget w : widgetList) {
    doSomething(w);
  }
}

...

This compliant solution wraps the ArrayList in a synchronized collection so that all modifications are subject to the locking mechanism.

Code Block
bgColor#ccccff

List<Widget> widgetList = 
    Collections.synchronizedList(new ArrayList<Widget>());

public void widgetOperation() {
  synchronized (widgetList) { // Client-side locking
    for (Widget w : widgetList) {
      doSomething(w);
    }
  }
}

This approach must be implemented correctly to avoid starvation, deadlock, and scalability issues [Goetz 2006a].

...

This compliant solution creates a deep copy of the mutable widgetList before iterating over it.

Code Block
bgColor#ccccff

List<Widget> widgetList = new ArrayList<Widget>();

public void widgetOperation() {
  List<Widget> deepCopy = new ArrayList<Widget>();
  synchronized (widgetList) { // Client-side locking
    for (Object obj : widgetList) {
      deepCopy.add(obj.clone());
    }
  } 

  for (Widget w : deepCopy) {
    doSomething(w);
  }
}

...

The CopyOnWriteArrayList data structure implements all mutating operations by making a fresh copy of the underlying array. It is fully thread-safe and is optimized for cases where traversal operations vastly outnumber mutations. Note that traversals of such lists always see the list in the state it had at the creation of the iterator (or enhanced for loop); subsequent modifications of the list are invisible to an ongoing traversal. Consequently, this solution is inappropriate when mutations of the list are frequent or when new values should be reflected in ongoing traversals.

Code Block
bgColor#ccccff

List<Widget> widgetList = new CopyOnWriteArrayList<Widget>();

public void widgetOperation() {
  for (Widget w : widgetList) {
    doSomething(w);
  }
}

...

[API 2006]

Class ConcurrentModificationException

[SDN 2008]

Sun Bug database, Bug ID 6687277

[Goetz 2006a]

5.1.2. Iterators and ConcurrentModificationException

 

      49. Miscellaneous (MSC)