Правильный способ удаления элементов из синхронизированных списков - PullRequest
1 голос
/ 27 февраля 2012

У меня есть стиль клиент-сервер.Что я сделал, так это создал класс RequestController, который контролирует и контролирует все запросы к серверу, выполненные в виде потокового объекта.

Эти потоки запросов отслеживаются в синхронизированном Map (создан с помощью Collections.synchronizedMap(new HashMap<Long, RequestThread>())).Каждые полсекунды проверяется каждый поток запроса, если он все еще жив, он отправляет сообщение о ходе выполнения своему заинтересованному объекту прослушивателя (атрибут потока запроса), если он завершил или потерпел неудачу, он уведомляет заинтересованный прослушиватель и удаляет себя из синхронизированного Map когда завершается метод run().

Объект RequestThread, который я использую для отслеживания каждого запроса, является защищенным внутренним классом объекта RequestController, который отслеживает все запросы.Так что у него есть доступ к карте, которая его содержит.

Именно здесь у меня возникают проблемы.Я получаю параллельные исключения при модификации, когда «мертвая нить» удаляется с карты.Весь мой доступ к карте синхронизирован, поэтому я не понимаю, как получить исключение.

Вот код цикла управления:

this.reqTimer = new Timer("Request Timer", true);
TimerTask reqTask = new TimerTask()
{
    @Override
    public void run()
    {
        synchronized(reqList)
        {
            for(RequestThread rt : reqList.values())
                rt.updateProgress();
        }
    }
};
this.reqTimer.scheduleAtFixedRate(reqTask, 500L, 500L);

В самом конце RequestThread.run() метод есть вызов метода RequestController для удаления себя с карты следующим образом:

   public void removeRequest(long id)
   {
       synchronized(this.reqList)
       {
           this.reqList.remove(id);
       }
   }

Похоже, что если RequestThread.run() завершается, пока метод TimerTask.run() проходит по картезапросов на вызов метода updateProgress() удаление не блокируется, но разрешено изменять карту и вызывать мое исключение.Как два разных потока могут блокировать один и тот же объект одновременно?Разве удаление не должно быть заблокировано, поскольку оно происходит в другом потоке до завершения обновления?

Ответы [ 2 ]

2 голосов
/ 27 февраля 2012

Вам либо нужно выполнить удаления после итерации по списку: -

  synchronized (reqList) {
    // Keep track of items to be removed.
    List<RequestThread> remove = new LinkedList<RequestThread>();
    for (RequestThread rt : reqList.values()) {
      rt.updateProgress(remove);
    }
    // Remove them.
    reqList.removeAll(remove);
  }

или вам нужно использовать Iterator и использовать метод remove.

2 голосов
/ 27 февраля 2012

Я собираюсь превратить мои комментарии в ответ.

Как два разных потока могут одновременно блокировать один и тот же объект?

Они не могут. Параллельная модификация должна быть где-то еще. Я подозреваю, что ваш updateProgress() делает то, что делает удаление из списка. Это может быть, когда происходит исключение одновременной модификации.

for (RequestThread rt : reqList.values()) {
    // you can't make any changes to reqList inside of the loop
    rt.updateProgress();
}

Если вам нужно updateProgress() удалить запрос из списка, вы можете:

  • Используйте итератор и передайте итератор в updateProgress(), чтобы он мог вызвать iterator.remove().
  • Передайте List<RequestThread> в updateProgress() и добавьте запросы, которые должны быть удалены. Затем, находясь вне цикла for (но все еще в синхронизированном блоке), вы можете удалить элементы из списка удаления из reqList.
...