我正在处理来自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/AndreidRuntime(28135):致命例外:线程-369 12-10 14:08:42.071:E/AndreidRuntime(28135):java.util.并发修改异常12-10 14:08:42.071:E/AndreidRuntime(28135):在java.util.LinkedHashMap$LinkedHashIterator.next条目(LinkedHashMap.java:347)12-10 14:08:42.071:E/AndreidRuntime(28135):在java.util.LinkedHashMap$KeyIterator.next(LinkedHashMap.java:367)12-10 14:08:42.071:E/AndreidRuntime(28135):在es.irchispano.chat.Main活动$6.run(MainActivity.java:244)12-10 14:08:42.071:E/AndreidRuntime(28135):在java.lang.Thread.run(Thread.java:841)
注:244行对应于
final String tabname = iter.next();
陈述
在我看来,这很奇怪,因为我使用的是一个系列。synchronizedMap和处理这些行的方法是同步的,那么为什么它仍然在发生呢?
谢谢
----------编辑----------
很抱歉最初的代码很简洁;我已经尽可能地简化了,但显然这不是一个好主意。我正在粘贴实际的代码。当然,这些结构中的每一个都是初始化的(否则我就不会有问题了:-)),现在我要带着良心阅读你们所有的评论,我会发布我将会发现的东西。谢谢大家的支持!
本例中的同步关键字在类MainActive处获取锁,然后该方法启动一个新线程并立即释放锁。
不能保证在处理新请求之前第一个事件的迭代已经结束。
新请求可能会导致两个线程同时遍历映射。
如果在方法中有对映射的修改操作,这将导致一个线程修改映射,而另一个线程仍在迭代它,从而导致并发修改异常。
使用集合。synchronizedMap
只意味着多个线程可以同时调用get
/put
/delete
。当迭代器在
集合上迭代时,
集合
发生更改时,它无法防止发生错误。即使在单个线程中也可能发生这种情况,例如,如果映射中至少有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
方法是不同步的。但是如果选项卡
没有被修改,那么这不是错误的来源。
您尚未显示
选项卡
的修改位置。这是因为在迭代器对映射进行迭代时,映射正在被修改,从而导致了异常。
修复方法是对其进行迭代,并使用相同的锁对其进行修改。
您已经向我们展示了mapTabs
是如何创建的(Java命名约定要求它的名称以小写字母开头),但没有展示它是如何填充的。实际上,map将始终为空,而当循环将运行零次。此外,本地变量tabname
未使用,但在实际代码中可能并非未使用。
也就是说,ProcessEvent
似乎将为每个事件运行一次。它是静态的、同步的,这意味着它将获得main活动的监视器。类,
以及在同一对象上同步的其他方法不能同时运行。
但是,它会启动一个新的线程,执行实际的工作,并立即返回。该线程不同步,因此任何数量的工作线程都可以同时运行,都使用相同的映射。映射用Collections.synchronizedMap
包装,这是防止并发修改的唯一保护。
这种同步映射不允许多个调用者同时调用其方法,但对不同方法的单独调用可以任意交错。例如,当一个调用者将新条目放入映射时,其他调用者无法访问映射。然而,一个调用者可以从映射中获取密钥集,从密钥集中获取迭代器,并开始对其进行迭代,然后另一个线程中的另一个调用者添加、修改或删除条目,最后,第一个线程继续对密钥集进行迭代,并获得ConcurrentModificationException
我建议使用
java.util.concurrent.Con相当HashMap
来代替,并从ProcessCase中删除同步关键字。
问题内容: 在Java中制作异步方法的同步版本的最佳方法是什么? 假设您有一个使用以下两种方法的类: 您将如何实现直到任务完成才返回的同步? 问题答案: 看看CountDownLatch。您可以使用以下类似方式模拟所需的同步行为: 您还可以通过2个参与者使用相同的行为,如下所示: 但是,如果您可以控制I 的源代码,建议您重新设计它以返回一个对象。这样,您可以在需要时轻松地在异步/同步行为之间进行切
同步调用异步方法最安全的方法是什么?
我正试图从同步方法运行异步方法。但是我不能等待异步方法,因为我在同步方法中。我一定不理解TPL,因为这是我第一次使用它。 每个方法都需要前一个方法来完成,因为第一个方法的数据用于第二个方法。 Await运算符只能在异步方法中使用。考虑用'async'修饰符标记此方法,并将其返回类型更改为'task' 但是,如果我使用async修饰符,这将是一个异步操作。因此,如果我对的调用没有使用await运算符
本文向大家介绍java synchronized同步静态方法和同步非静态方法的异同,包括了java synchronized同步静态方法和同步非静态方法的异同的使用技巧和注意事项,需要的朋友参考一下 java synchronized 详解 synchronized关键字有两种用法,一种是只用于方法的定义中,另外一种是synchronized块,我们不仅可以使用synchronized来同步一个对
本文向大家介绍不同js异步函数同步的实现方法,包括了不同js异步函数同步的实现方法的使用技巧和注意事项,需要的朋友参考一下 不同函数达到同步的函数模拟 funcList是函数执行函数的队列,其中回调函数中flag=true是同步标记量 以上这篇不同js异步函数同步的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持呐喊教程。
本文向大家介绍JS同步、异步、延迟加载的方法,包括了JS同步、异步、延迟加载的方法的使用技巧和注意事项,需要的朋友参考一下 本文讲述了JS同步、异步、延迟加载的方法。分享给大家供大家参考,具体如下: 一:同步加载 我们平时使用的最多的一种方式。 <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续