Предлагает ли контейнер Java отказоустойчивый итератор
Вопрос
Вот в чем моя проблема:
Этот фрагмент кода выдает java.util.ConcurrentModificationException
, потому что Vector
listeners
изменяется, пока существует Iterator
для этой структуры данных.В java-doc говорится, что этот контейнер предлагает только безотказный итератор.
Есть ли возможность получить Iterator
поверх стандартного контейнера, такого как Vector
или List
в Java, который предлагает мне Iterator
, который не становится недействительным (не является безотказным), если во время этого есть элемент, удаленный Iterator
"жизни"?
У меня должно быть такое же поведение, как у std::list
на C++.Там итератор всегда действителен, даже если текущий итератор удален.Затем итератор устанавливается на следующий элемент в списке.
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);
}
}
Решение
Насколько я знаю, нет никакого способа задним числом добавить эту возможность к любому значению по умолчанию Collection
реализация (Iterable
на самом деле).
Но существуют реализации, которые поддерживают такое поведение, имея четко определенные ответы на одновременное изменение во время итерации.
Одним из примеров является CopyOnWriteList
.
Другие советы
В случае слушателей вы могли бы подумать об использовании java.util.concurrent.CopyOnWriteArrayList
поскольку обычно у вас намного больше операций чтения, чем записи.
взгляните на пакет java.util.concurrent, и вы найдете там все, что вам нужно.
Ленивый способ создания быстрого, безотказного итератора:возьмите копию списка в виде массива, пока он заблокирован, и foreach() поверх массива, пока он разблокирован...Может быть сделано с любым типом списка
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();
}