Проблема параллелизма в Java с итератором и вложенной Hashtable - PullRequest
1 голос
/ 30 января 2011

Доброе утро, надеюсь получить помощь от кого-то, кто может знать немного больше о Java, чем я. Я пришел из .NET фона, и мне было поручено отследить проблему в поставляемом поставщиком решении. Я верю, что нашел это, но хотел бы второе, третье или четвертое мнение, если это возможно.

Что я думаю, что происходит, так это то, что строка номер 108 (указанная в блоке кода ниже) модифицирует Iterator (memIter), который был объявлен внешним по отношению к циклу while. Они модифицируют его, изменяя экземпляр, который был объявлен внутренним для цикла, а не исходный объект, и я верю, что он выбрасывает, потому что «next» вызывается на второй итерации в измененной коллекции / hastbale. На этом сайте я нашел несколько потоков, которые указывают на это (/506574/pochemu-vybrasyvaetsya-isklychenie-concurrentmodificationexception-i-kak-ego-otlazhivat), но потому, что он изменяет коллекцию (извините, если это термины .net) внутри коллекции (он удаляет члена из свойства hastable элемента в итератор) я бы предположил, что применима та же логика, но это не мое пространство. Также, если мое предположение верно, может ли кто-нибудь предоставить правильную реализацию?

STACK

java.util.ConcurrentModificationException трассировки стека: java.util.ConcurrentModificationException в java.util.HashMap $ HashIterator.nextEntry (HashMap.java:793) в java.util.HashMap $ ValueIterator.next (HashMap.java:822) в xxx.xxxxx.xx.xxxxxxx.end (RoleOrganizer.java:108) в (xxxxxxxxx.java:568) в xxx.xxxx.xxxxxxx.handleRequest (xxxxHandler.java:74) в com.xxxxx.server.JavaInstanceMethod.execute (JavaInstanceMethod.java:33) в xx.xxxxxxx.execute (AppServer.java:1469) в xxx.xx.executeRequest (xxxxxjava: 1269) на xxx.xxxxx.server.xxxx.doGet (xxxxx.java:350) на javax.servlet.http.HttpServlet.service (HttpServlet.java:690) на javax.servlet.http.HttpServlet.service (HttpServlet.java:803) в org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:290) в org.apache.catalina.core.ApplicationFilterChain.doFilter (ApplicationFilterChain.java:206) в org.apache.catalina.core.StandardWrapperValve.invoke (StandardWrapperValve.java:233) в org.apache.catalina.core.StandardContextValve.invoke (StandardContextValve.java:175) в org.apache.catalina.core.StandardHostValve.invoke (StandardHostValve.java:128) в org.apache.catalina.valves.ErrorReportValve.invoke (ErrorReportValve.java:102) в org.apache.catalina.core.StandardEngineValve.invoke (StandardEngineValve.java:109) в org.apache.catalina.connector.CoyoteAdapter.service (CoyoteAdapter.java:286) в org.apache.jk.server.JkCoyoteHandler.invoke (JkCoyoteHandler.java:190) на org.apache.jk.common.HandlerRequest.invoke (HandlerRequest.java:283) на org.apache.jk.common.ChannelSocket.invoke (ChannelSocket.java:767) на org.apache.jk.common.ChannelSocket.processConnection (ChannelSocket.java:697) в org.apache.jk.common.ChannelSocket $ SocketConnection.runIt (ChannelSocket.java:889) в org.apache.tomcat.util.threads.ThreadPool $ ControlRunnable.run (ThreadPool.java:690) на java.lang.Thread.run (Thread.java:619)

Код ниже с номером строки ошибки стека 108

Строка 87 конец void ()

{

Iterator iter = new ArrayList(this.m_member.getRoles()).iterator();

while (iter.hasNext())

{

  UserType rt = (UserType)iter.next();
  if (!this.m_roleMap.containsKey(rt.getGID()))
  {
    this.m_member.removeRole(ut);
  }
}
iter = this.m_roleMap.values().iterator();

Линия 99
while (iter.hasNext ())

{
  UserType ut = (UserType)iter.next();
  if (ut.isUnique())
  {
    Iterator memIter = this.m_member.doTask().lookUpMembers().iterator();
    while (memIter.hasNext())
    {

Линия 108
StoreMember mem = (StoreMember) memIter.next ();

      if (mem.doWork() != this.m_member.getId())
      {
        if ((mem.hasRole(ut)) && (!mem.isFormer()))
        {
          mem.removeRole(ut);
        }
      }
    }
  }

Ответы [ 3 ]

1 голос
/ 30 января 2011

Вы не можете изменять коллекцию во время итерации по ней. Решение состоит в том, чтобы использовать объект Iterator и напрямую вызывать remove.

Использование ConcurrentHashMap помогает, потому что оно не генерирует исключение ConcurrentModificationException. Из ConcurrentHashMap:

Операции поиска (включая получение) как правило, не блокируют, поэтому могут перекрываться с операциями обновления (включая пут и удалить). Извлечения отражают результаты самых последних завершенных обновить операции, держась за их начало. Для совокупных операций таких как положено все и ясно, одновременно извлечения могут отражать вставку или удаление только некоторых записей. Аналогично, итераторы и перечисления возвратные элементы, отражающие состояние хэш-таблицы в какой-то момент в или с момента создания Итератор / перечисление. Они не бросить ConcurrentModificationException. Тем не менее, итераторы предназначены для используется только одним потоком за раз.

1 голос
/ 30 января 2011

я думаю, что строка 108 (указанная в кодовом блоке ниже) модифицирует итератор

Я подозреваю, что вы неправильно поняли, при каких обстоятельствах вы получаете ConcurrentModificationException.

A ConcurrentModificationException не из-за одновременной модификации итератора, а из базовой коллекции.

Из документации:

Это исключение может быть вызвано методами, которые обнаружили одновременную модификацию объекта, когда такая модификация недопустима.

Например, , как правило, один поток не может изменять коллекцию, пока другой поток итерирует по ней .

0 голосов
/ 30 января 2011

Похоже, что проблема заключается в изменении коллекции во время итерации по ней, хотя сложно точно следить за тем, что происходит, не видя doTask и lookUpMembers.(Обратите внимание, что та же логика будет применяться в .NET.)

Два возможных исправления (применимо только одно!):

  • Скопируйте результат lookUpMembers либо в lookUpMembersсам или где вы его используете.Создайте из него новый список, и тогда не будет иметь значения, удаляете ли вы элементы из исходной коллекции.
  • Создайте список ролей для удаления в цикле, а затем повторите цикл отдельно, чтобы удалить их.

Лично я бы, вероятно, выбрал первое, потому что это означает, что вам нужно внести изменения только один раз.Результат вызова doTask().lookUpMembers() не звучит , как будто он напрямую поддерживается исходной коллекцией.В любом случае, вы должны четко задокументировать, что вы делаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...