我正在处理来自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();
}
...
}大多数情况下,这是正确的,但有时会有一些雪崩事件,我得到了一个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行对应于
final String tabname = iter.next();语句。
在我看来很奇怪,因为我使用的是一个Collections.synchronizedMap,处理这些行的方法是同步的,那么为什么仍然会发生这种情况呢?
谢谢!
抱歉,最初的代码很简洁;我已经尽量简化了,但显然这不是个好主意。我在粘贴实际代码。当然,所有这些结构都是初始化的(否则我就不会有问题了:-),现在我将带着良心阅读您的所有评论,我将发布我将发现的内容。谢谢大家的支持!
发布于 2013-12-10 19:56:49
您已经向我们展示了如何创建map Tabs (Java命名约定会指定它的名称以小写字母开头),而不是如何填充它。实际上,映射始终是空的,while循环将运行0次。此外,局部变量tabname未使用,但在实际代码中可能未使用。
尽管如此,ProcessEvent似乎将在每个事件中运行一次。它是静态的,并且是同步的,这意味着它将获得MainActivity.class,的监视器,而在同一对象上同步的任何其他方法都不能同时运行。
但是,它启动一个新线程,执行实际工作,并立即返回。该线程是不同步的,因此任何数量的这些工作线程都可以同时运行,它们都使用相同的映射。映射是用Collections.synchronizedMap包装的,这是防止并发修改的唯一保护。
这样的同步映射将不允许多个调用方同时调用其方法,但对不同方法的单独调用可以任意交织。例如,当一个调用者将一个新条目放入映射时,没有其他调用方可以访问该映射。但是,一个调用方可以从映射中获取密钥集,从密钥集中获取迭代器,并开始对其进行迭代,然后由另一个线程中的另一个调用方添加、修改或删除一个条目,最后,第一个线程继续对密钥集进行迭代并获得一个ConcurrentModificationException.。
我建议改用java.util.concurrent.ConcurrentHashMap,并从ProcessEvent.中移除同步关键字
发布于 2013-12-10 19:51:31
本例中的同步关键字在类MainActivity处获取一个锁,然后该方法启动一个新线程并立即释放锁。
在处理新请求之前,不能保证第一个事件的迭代已经结束。
新请求可能导致两个线程同时遍历映射。
如果在方法doSomeAdditionalStuff()中映射上有修改操作,这将导致一个线程修改映射,而另一个线程仍在遍历映射,从而导致ConcurrentModificationException。
发布于 2013-12-10 19:57:40
对Tabs的访问不受任何锁的保护。访问runnable.run()的Tabs也不受任何锁的保护。您的代码允许并行生成多个线程。由于每个线程的可运行性都可以访问您的映射(Tabs),所以其中一个线程有可能修改该映射,而另一个线程则迭代它。这将导致您正在看到的ConcurrentModificationException。
https://stackoverflow.com/questions/20503627
复制相似问题