有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

同步方法上的java ConcurrentModificationException

我正在处理很多来自TCPsocket的事件(每秒10个),所以我使用多线程处理这些事件

public class MainActivity extends Activity {
  ...

  // In this Map I store the tab name and the associated TabHost.TabSpec instance
  private static Map<String, TabHost.TabSpec> Tabs = Collections.synchronizedMap(new LinkedHashMap<String, TabHost.TabSpec>());
  // In this Map I store pairs of tab-names and a HashSet of undelivered messages
  // It's a class that extends Map<String, HashSet<String>> with some additional functions that doesn't have anything to do with Tabs.
  private static NamesListing Names = Collections.synchronizedMap(new LinkedHashMap<String, HashSet<String>>());

  // Yes, I know the names don't follow the Java standards, but I keeped them for mantaining the question coherence. I will change it in my code, though.

  synchronized private static void ProcessEvent(final String name, final String message) {
    // Low-priority thread
    final Thread lowp = new Thread(
      new Runnable() { 
        public void run() {
          final Iterator<String> iter = Tabs.keySet().iterator();
          while (iter.hasNext()) {
            final String tabname = iter.next();
            // This just returns an int making some calculations over the tabname
            final int Status = Names.getUserStatus(tabname, message);

            // Same than getUserStatus
            if ((Names.isUserSpecial(Status)) && (name.equals(tabname))) {
              // This just removes a line from the HashSet
              Names.delLine(tabname, message);
            }
          }
        }
      });
    lowp.setPriority(3);
    lowp.start();
  }

  ...
}

大多数情况下,这是正确的,但有时会出现这些事件的雪崩,有时我会得到一个ConcurrentModificationException:

12-10 14:08:42.071: E/AndroidRuntime(28135): FATAL EXCEPTION: Thread-369 12-10 14:08:42.071: E/AndroidRuntime(28135): java.util.ConcurrentModificationException 12-10 14:08:42.071: E/AndroidRuntime(28135): at java.util.LinkedHashMap$LinkedHashIterator.nextEntry(LinkedHashMap.java:347) 12-10 14:08:42.071: E/AndroidRuntime(28135): at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:367) 12-10 14:08:42.071: E/AndroidRuntime(28135): at es.irchispano.chat.MainActivity$6.run(MainActivity.java:244) 12-10 14:08:42.071: E/AndroidRuntime(28135): at java.lang.Thread.run(Thread.java:841)

注:244行对应于

final String tabname = iter.next();

声明

在我看来,这很奇怪,因为我使用的是一个系列。synchronizedMap和处理这些行的方法是同步的,那么为什么它仍然在发生呢

谢谢

------编辑------------

抱歉,最初的代码很简洁;我试图尽可能地简化,但显然这不是一个好主意。我正在粘贴实际代码。当然,这些结构中的每一个都已初始化(否则我就不会有问题了:-)),现在我将带着良心阅读您的所有评论,并发布我将发现的内容。谢谢大家的支持


共 (2) 个答案

  1. # 1 楼答案

    使用Collections.synchronizedMap只意味着多个线程可以同时调用get/put/delete。它不能防止CollectionIterator对其进行迭代时发生的错误。即使在单个线程中也可能发生这种情况,例如,如果映射中至少有3个元素(我认为),那么下面的线程应该抛出它:

    final Iterator<String> iter = Tabs.keySet().iterator();
    iter.next(); 
    String k = iter.next();
    final Iterator<String> iter2 = Tabs.keySet().iterator();
    iter2.next();
    Tabs.delete(k);
    iter2.next();
    

    现在,正如其他人指出的那样,多个线程可以同时在Map上迭代,因为run方法是不同步的。但是如果Tabs没有被修改,那么这就不是错误的来源

    您还没有显示Tabs在哪里被修改这是因为在迭代器对其进行迭代时,映射正在被修改,导致了异常

    修复方法是对其进行迭代,并使用相同的锁对其进行修改

  2. # 2 楼答案

    Tabs的访问不受任何锁的保护。有权访问Tabsrunnable.run()也不受任何锁的保护。您的代码允许并行生成多个线程。因为每个线程的runnable都可以访问映射(Tabs),所以其中一个线程有可能在另一个线程迭代映射时修改该映射。这将导致您看到的ConcurrentModificationException