Java 同步方法上的ConcurrentModificationException

Java 同步方法上的ConcurrentModificationException,java,android,multithreading,Java,Android,Multithreading,我正在处理许多来自TCP套接字的事件(每秒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.sync

我正在处理许多来自TCP套接字的事件(每秒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();
  }

  ...
}
声明

当我使用Collections.synchronizedMap并且处理这些行的方法是同步的时,我觉得很奇怪,为什么它仍然在发生

谢谢

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


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

本例中的synchronized关键字获取MainActivity类的锁,然后该方法启动一个新线程并立即释放锁

无法保证在处理新请求之前,第一个事件的迭代已经结束

新请求可能导致两个线程同时在映射中迭代


如果在方法doSomeAdditionalStuff()中对映射进行了修改操作,这将导致一个线程修改映射,而另一个线程仍在对其进行迭代,从而导致ConcurrentModificationException。

您已经向我们展示了映射
选项卡的创建方式(Java命名约定将指定其名称以小写字母开头),但不指定其填充方式。事实上,映射始终为空,while循环将运行零次。此外,局部变量
tabname
未使用,但在实际代码中可能未使用

这就是说,
ProcessEvent
似乎将为每个事件运行一次。它是静态的,并且是同步的,这意味着它将获得
MainActivity.class、
的监视器,并且没有其他同步同一对象的方法可以同时运行

但是,它启动一个新线程,该线程执行实际工作,并立即返回。该线程不同步,因此可以同时运行任意数量的工作线程,所有线程都使用相同的映射。映射用
集合包装。synchronizedMap
是防止并发修改的唯一保护美国

这种同步映射不允许多个调用方同时调用其方法,但对不同方法的单独调用可以任意交错。例如,当一个调用方将新条目放入映射时,没有其他调用方可以访问该映射。但是,一个调用方可以从映射中获取密钥集get迭代器从密钥集开始迭代,然后让另一个线程中的另一个调用方添加、修改或删除条目,最后让第一个线程继续迭代密钥集并获得
ConcurrentModificationException。


我建议改用
java.util.concurrent.ConcurrentHashMap
,并从
ProcessEvent中删除synchronized关键字。
选项卡的访问不受任何锁的保护。
runnable.run()有权访问
选项卡的
也不受任何锁的保护。您的代码允许并行生成多个线程。因为,每个线程的runnable都有权访问您的映射(
选项卡
),其中一个线程可能在另一个线程对其进行迭代时修改该映射。这将导致您看到的
ConcurrentModificationException

使用
集合。synchronizedMap
仅意味着多个线程可以同时调用
get
/
put
在其上删除
。它不能防止当
迭代器在其上迭代时,
集合
发生更改时发生的错误。即使在单个线程中也可能发生这种情况,例如,如果映射中至少有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();
final Iterator iter=Tabs.keySet().Iterator();
iter.next();
字符串k=iter.next();
最终迭代器iter2=Tabs.keySet().Iterator();
iter2.next();
b.删除(k);
iter2.next();
现在,正如其他人指出的那样,多个线程可以同时在
Map
上迭代,因为
run
方法是不同步的。但是如果
Tabs
没有被修改,那就不是错误的来源

您没有显示在哪里修改
选项卡
。这是因为迭代器在其上迭代时修改了映射,导致了异常


修复方法是迭代并使用相同的锁修改它。

是否
doSomeAdditionalStuff()
修改地图?^因为这是关键:)@rgetman不,它对地图没有任何作用。我相信只有一个MainActivity实例,但它会启动多个工作线程,这些线程可能会互相践踏。来吧,伙计们,这个问题不值得否决。。给新手一些空气+1为了平衡,你一针见血,由于这个完整的解释,我能够进行一些额外的调试,并发现这个异常正是在处理另一种事件时发生的,这在后台添加了一个新的选项卡。因此,我将在记住本线程中所有有价值的响应的情况下进行必要的更改。我对所有有用的评论都投了赞成票,但这一条对我帮助最大,所以我接受它。谢谢大家!
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();