Предлагает ли контейнер 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);
}
}
4 ответа
Насколько я знаю, нет никакого способа задним числом добавить эту способность к любому значению по умолчанию. 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();
}