Не знаю, почему я получаю это исключение. Проблема в том, что иногда onClose
вызывается до closeAllOpenSessions
. onClose
работает нормально, удаляя указанный элемент из _openSessions
. Однако, когда closeAllSessions
называется ConcurrentModificationException
генерируется, когда мы пытаемся выполнить цикл над _openSessions.
Исключение не возникает, когда я комментирую инструкцию remove. Учитывая, как обе эти операции защищены одним и тем же мьютексом, я не уверен, почему это происходит. Что мне не хватает?
class B
{
protected void onClose(Session session, List<WebSocketSessionStateful> openSessions) throws IOException
{
Iterator<WebSocketSessionStateful> openSessionsElements = openSessions.iterator();
while(openSessionsElements.hasNext())
{
WebSocketSessionStateful openSession = openSessionsElements.next();
if(session.equals(openSession.getSession()))
{
openSessionsElements.remove(); // comment me out to not throw exception
return;
}
}
}
protected static void closeAllOpenSessions(List<WebSocketSessionStateful> openSessions) throws IOException
{
for(WebSocketSessionStateful openSession : openSessions) // exception generated here
{
openSession.getSession().close(new CloseReason(CloseReason.CloseCodes.GOING_AWAY, ""));
}
}
}
class A extends B
{
private static final Object _endpointOperationMutex = new Object();
private static final List<WebSocketSessionStateful> _openSessions = new ArrayList<WebSocketSessionStateful>();
public void onClose(Session session, EndpointConfig config) throws IOException
{
synchronized (_endpointOperationMutex)
{
super.onClose(session, _openSessions);
}
}
static void closeSessions() throws IOException
{
synchronized (_endpointOperationMutex)
{
WebSocketEndpointBase.closeAllOpenSessions(_openSessions);
}
}
}
В вашем closeAllOpenSesions()
вы openSessions
свою коллекцию openSessions
и, для каждого элемента в ней, вызываете close()
, что, конечно же, вызывает onClose()
метода onClose()
. Затем вы также выполняете итерацию и удаление из openSessions
с каждым вызовом этого onClose()
которого происходит ваша параллельная модификация.
Вы можете более четко сформулировать проблему следующим образом:
Iterator outerLoop = coll.iterator();
while (outerLoop.hasNext) {
Iterator innerLoop = coll.iterator();
while (innerLoop.hasNext()){
innerLoop.remove(); //ConcurrentModificationException
}
}
Это ничем не отличается от:
for (Object o : coll)
for (Object p : coll)
if (p.someBoolean()) remove(p); //ConcurrentModificationException
Вы не можете одновременно использовать два итератора, повторяя одну и ту же коллекцию. Если вы это сделаете, будет ConcurrentModificationException
, если внутренний цикл удалит элемент, который ожидает внешний цикл.
С openSessions
взгляда я не понимаю, зачем вам нужно перебирать openSessions
в B.onClose()
. Почему B.onClose()
несет ответственность за закрытие всех открытых сеансов? Не должно ли это быть сделано в closeAllOpenSessions()
?