Неожиданный ConcurrentModificationException

1

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

}
  • 0
    Если вы удаляете элемент из списка во время итерации по списку, вы получаете исключение ConcurrentModificationException.
Теги:
multithreading
thread-safety
concurrency
concurrentmodification

1 ответ

2
Лучший ответ

В вашем 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()?

  • 0
    Хороший улов на openSession.getSession (). Close, вызывающий onClose (). Я думал, onClose () будет вызываться только в ответ на закрытие соединения клиентом, а не сервером. Но, похоже, он вызывается, когда сервер также закрывает соединение. В этом свете одновременная модификация имеет смысл для меня. Благодарю.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню