首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >同步法上的ConcurrentModificationException

同步法上的ConcurrentModificationException
EN

Stack Overflow用户
提问于 2013-12-10 19:36:32
回答 4查看 2K关注 0票数 2

我正在处理来自TCP套接字的许多事件(设置为每秒10次),所以我使用多线程来处理这些事件。

代码语言:javascript
复制
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):致命异常:线程-369 12-10 14:08:42.071: E/AndroidRuntime(28135):java.util.ConcurrentModificationException 12-1014:08:42.071: E/AndroidRuntime(28135):at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:367 12-1014:08:42.071: E/AndroidRuntime(28135):at java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:367) 12-10 14:08:42.071: E/AndroidRuntime(28135):在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行对应于

代码语言:javascript
复制
final String tabname = iter.next();

语句。

在我看来很奇怪,因为我使用的是一个Collections.synchronizedMap,处理这些行的方法是同步的,那么为什么仍然会发生这种情况呢?

谢谢!

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-12-10 19:56:49

您已经向我们展示了如何创建map Tabs (Java命名约定会指定它的名称以小写字母开头),而不是如何填充它。实际上,映射始终是空的,while循环将运行0次。此外,局部变量tabname未使用,但在实际代码中可能未使用。

尽管如此,ProcessEvent似乎将在每个事件中运行一次。它是静态的,并且是同步的,这意味着它将获得MainActivity.class,的监视器,而在同一对象上同步的任何其他方法都不能同时运行。

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

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

我建议改用java.util.concurrent.ConcurrentHashMap,并从ProcessEvent.中移除同步关键字

票数 4
EN

Stack Overflow用户

发布于 2013-12-10 19:51:31

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

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

新请求可能导致两个线程同时遍历映射。

如果在方法doSomeAdditionalStuff()中映射上有修改操作,这将导致一个线程修改映射,而另一个线程仍在遍历映射,从而导致ConcurrentModificationException。

票数 1
EN

Stack Overflow用户

发布于 2013-12-10 19:57:40

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20503627

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档