Qué ofrece contenedor Java un iterador a prueba de fallos
Pregunta
Aquí está mi problema:
Esta pieza de código lanza una java.util.ConcurrentModificationException
, porque el Vector
listeners
se modifica mientras existe una Iterator
para esta estructura de datos.
El java-doc dice que este contenedor sólo ofrece un iterador de fail-fast.
¿Existe la posibilidad de obtener una Iterator
sobre un contenedor estándar como Vector
o List
en Java que me ofrece una Iterator
, que no recibe no válido (no es a prueba de rápido), si hay un elemento eliminado durante ese Iterator
" vidas "?
Yo debería tener el mismo comportamiento como el std::list
en C ++. Allí, el iterador es siempre válida incluso si el repetidor actual se quite. Que el iterador se establece en el siguiente elemento de la lista.
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);
}
}
Solución
Por lo que yo sé que no hay manera de añadir retroactivamente esa capacidad de cualquier implementación Collection
defecto (Iterable
de hecho).
Sin embargo, existen implementaciones que soporte ese tipo de comportamiento por tener respuestas bien definidas a la modificación concurrente, mientras que la iteración.
Un ejemplo es el CopyOnWriteList
.
Otros consejos
En el caso de los oyentes, se podría pensar en usar java.util.concurrent.CopyOnWriteArrayList
como lo hace normalmente tiene forma más lecturas que escrituras.
have a look at java.util.concurrent package you will find everything you need.
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();
}