1

Here is my problem:

This piece of code throws a java.util.ConcurrentModificationException, because the Vector listeners is modified while there exists an Iterator for this data structure. The java-doc says that this container offers only a fail-fast iterator.

Is there a possibility to get an Iterator over a standard container like Vector or List in Java that offers me an Iterator, that does not get invalid (is not fail-fast), if there is an element removed during that Iterator "lives"?

I should have the same behavior like the std::list in C++. There the iterator is always valid even if the current iterator is remove. Than the iterator is set to the next element in the list.

public class ClientHandle {
private final Vector<ClientHandleListener> listeners = new Vector<ClientHandleListener>();


public synchronized void  addListener(ClientHandleListener chl) {
    listeners.add(chl);
}

public synchronized void  removeListener(ClientHandleListener chl) {
    listeners.remove(chl); 
}

private void fireConnectionClosed() {
    final ClientHandle c = this;

    final Iterator<ClientHandleListener> it = listeners.iterator();
    new Thread(){
        @Override
        public void run() {
            while (it.hasNext()) {
                it.next().connectionClosed(c); //FIXME the iterator gets modified 
            }
            };
    }.start();
}}

public class ClientHandlePool implements ClientHandleListener, TaskManagerListener {

        /*...*/
    public synchronized void  removeClientHandle(ClientHandle ch) {
                //here the listeners Vector from the ClientHandle gets modified
        ch.removeListener(this); 
        ch.removeListener(currentListener);
        clientHandles.remove(ch);
    }

    @Override
    public void connectionClosed(ClientHandle ch) {
        removeClientHandle(ch);
    }
}
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
nutario
  • 773
  • 1
  • 9
  • 15
  • 1
    your problem is using Vector to begin with don't use Vector use a List, Vectors and Hashtables are BAD practice and old. See the java.util.concurrent package. –  Mar 01 '10 at 18:46
  • 2
    @fuzzy: `Vector` and `Hashtable` are not necessarily bad. When you need their specific features (mostly synchronization), then they have a valid place. – Joachim Sauer Mar 01 '10 at 18:48
  • 1
    @joachim Well, that's why there is the java.util.concurrent package, as fuzzy already said. The CopyOnWriteArrayList is pretty useful, when you have to deal with concurrent modifications- *edit*: In addition to that there's also the java.util.Collections.synchronizedXYZ methods which basically create a synchronized copy of your List/Map/Set/etc... You'll still have to use synchronized blocks, though – Tedil Mar 01 '10 at 18:57
  • if you need thread safe Lists or Maps you use the thread safe wrappers. Vector and Hashtable are old and busted and look really bad in modern code. They show that the developer is not keeping up with the times and best practices. They are there for backwards compatibility only. –  Mar 01 '10 at 19:03
  • @fuzzy: I know that there are other alternatives, but what exactly makes a `Vector` worse than a `ArrayList` wrapped using `Collections.synchronizedList()`? What exactly does "busted" mean here? Software doesn't bitrot. And if you are so sure that they are there for backwards compatibility, then I wonder why they are not deprecated yet. – Joachim Sauer Mar 01 '10 at 19:16
  • see this http://stackoverflow.com/questions/453684/use-hashtable-vector-or-hashmap-or-arraylist-in-java –  Mar 01 '10 at 19:30
  • @fuzzy: the accepted answer of that question claims that the local synchronization of `Vector` and `Hashtable` is their biggest flaw. However, an `ArrayList` wrapped using `Collections.synchronizedList()` is only locally synchronized as well. It still needs external synchronization when iterating over it (exactly what this question is about, to come at least somewhat back on-topic). – Joachim Sauer Mar 01 '10 at 19:39

4 Answers4

8

As far as I know there's no way to retroactively add that ability to any default Collection implementation (Iterable in fact).

But there are implementations that support that kind of behaviour by having well-defined responses to concurrent modification while iterating.

One example is the CopyOnWriteList.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
6

In case of listeners, you might think about using java.util.concurrent.CopyOnWriteArrayList as you typically have way more reads than writes.

sfussenegger
  • 35,575
  • 15
  • 95
  • 119
2

have a look at java.util.concurrent package you will find everything you need.

Paul Whelan
  • 16,574
  • 12
  • 50
  • 83
0

A lazy way to create a fast, fail-safe iterator: take a copy of the list as an array while locked, and foreach() over the array while unlocked... Can be done with any type of List

private void fireConnectionClosed() {
   final ClientHandle c = this;

   final ClientHandleListener[] listenersArr;
   synchronized(this) {
       listenersArr=listeners.toArray(new ClientHandleListener[0]);
   }
   new Thread(){
       @Override
       public void run() {
          for(ClientHandleListener listener : listenersArr )
              listener.connectionClosed(c);
          }
       };
   }.start();
}
RedPandaCurios
  • 2,264
  • 12
  • 20