Reputation: 793
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);
}
}
Upvotes: 1
Views: 2846
Reputation: 2324
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();
}
Upvotes: 0
Reputation: 308131
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
.
Upvotes: 8
Reputation: 36095
In case of listeners, you might think about using java.util.concurrent.CopyOnWriteArrayList
as you typically have way more reads than writes.
Upvotes: 6
Reputation: 16809
have a look at java.util.concurrent package you will find everything you need.
Upvotes: 2