Java 同步方法上的ConcurrentModificationException
我正在处理许多来自TCP套接字的事件(每秒10个),因此我使用多线程处理这些事件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
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();