Concurrent modification exceptions Be Careful When Using Container Classes

public synchronized void trimToSize { modCount++; int oldCapacity = elementData.length; if elementCount oldCapacity { Object oldData[] = elementData; elementData = new Object[elementCount]; System.arraycopyoldData, 0, elementData, 0, elementCount; } } public synchronized void ensureCapacityint minCapacity { modCount++; ensureCapacityHelperminCapacity; } Because only one thread can modify an instance of Vector at a time, Vector is, technically speaking, threadsafe. I say technically speaking because while each individual method of Vector is threadsafe, this is rarely good enough. Just as with our bank accounts, you will often need to hold a lock over several method invocations in order to use Vector correctly. For example, suppose we only want to put an object into Vector if it isnt already in Vector . The following code is incorrect: public void insertIfAbsentVector vector, Object ob ject { if vector.containsobject { return; } vector.addobject; } If more than one thread is executing inside this method, then the same object can wind up being added to the Vector more than once. How? More than one thread can make the test before any of the threads add the object. Its not even enough to synchronize the method, as in the following version: public synchronized void insertIfAbsentVector vector, Object object { if vector.containsobject { return; } vector.addobject; } because this code implicitly assumes that there is only one instance of the class that contains the method. If there are two instances of the class, then there are two synchronization locks, one for each instance. Hence, the same object may be added to the instance of Vector twice. If there will be more than one thread manipulating an instance of Vector , you probably need to synchronize on the instance of Vector , as in the following code: public void insertIfAbsentVector vector, Object object { synchronizedvector { if vector.containsobject { return; } vector.addobject; } }

12.2.4.1 Concurrent modification exceptions

The previous example may seem a little contrived. However, consider the following piece of generic event-broadcasting code, which is similar to the code used to broadcast user-interface events in the JDK 1.1 event model: private void broadcastAnnouncement { Enumeration e = _listeners.elements ; while e.hasMoreElements { Listener nextListener = Liste ner e.nextElement ; nextListener.changeOccurred ; } } This isnt threadsafe either. We could easily wind up throwing an instance of NullPointerException if _listeners is being changed while the enumeration is in progress. And simply synchronizing on _listeners , as in the following version, doesnt quite solve the problem: private void broadcastAnnouncement { syncrhonized_listeners { Enumeration e = _listeners.elements ; while e.hasMoreElements { Listener nextListener = Listener e.nextElement ; nextListener.changeOccurred ; } } } This is because the same thread that owns the synchronization lock can still alter _listeners . That is, if the call to nextListener.changeOccurred results in a change to _listeners , and that change is made from within the same thread, then the lock wont prevent the change from happening. In Java 2, this problem is called a concurrent modification error. The newer Collection classes attempt to detect it and throw instances of ConcurrentModification - Exception when it occurs. For more information, see the description of the ConcurrentModificationException class in the java.util package and some of the discussions about fail-fast iterators found on the Internet. Preventing this sort of error can be difficult to do efficiently. A good, if slightly inefficient solution, is to simply make a local copy of the container in the methods that alter the container, as in the following code: public void addListenerListener listener { Vector copy; synchronized_vector { copy = Vector _vector.clone ; _vector = copy; } } private void broadcastAnnouncement { no change necessary to this code. syncrhonized_vector { Enumeration e = _listeners.elements ; while e.hasMoreElements { Listener nextListener = Listener e.nextElement ; nextListener.changeOccurred ; } } } This makes the modification to a new copy of _listeners . Meanwhile, the enumeration continues to use the old instance of Vector , which means that the instance of Vector the enumeration is using is not changing and cannot have a concurrent modification error. On the other hand, this can result in the creation of a large number of new objects. If the vector is modified frequently, this can be an expensive technique.

12.2.4.2 The other containers