我在实现一种线程安全的方式来集中更新和访问我的Android应用程序中的类对象列表时遇到了一些麻烦。这是一个开放式的问题,不能给出很多源代码。
基本上我有一个带有活动和服务的应用程序。该服务实现UDP广播任务和UDP广播监听任务。广播的信息本质上是表示设备的类的JSON序列化副本。例如UUID,IP,一些管理信息,如用户集名称,描述等...... JSON存储在UDP数据包中并发送,UDP数据包被接收和反序列化并处理。该反序列化类存储在Hashtable中,当前实例属于服务。例如每个想要访问数据的人都必须通过该服务。整件事情非常异常。
活动绑定到服务(通过和扩展Binder),因此它可以调用服务方法,如启动/停止UDP任务。当接收到任何更新或新设备数据时,Activity将侦听服务将发出的Android Intents,并且UI将显示与接收的UDP分组数据相关的信息。再次注意,分组数据存储在属于服务的容器类中。
问题是我无法找到一种合适的方法来使接收数据线程的Hashtable安全。从Service方法中获取数据然后处理数据时,我得到java.util.ConcurrentModificationException错误。如果在我在循环(迭代器或for)中处理数据时更新数据,则会发生ConcurrentModificationException。我知道在哪里,何时以及为什么,但是使用lock()或ReentrantLock()通常在具有要锁定数据的类的方法调用中使用,而不是在为该容器类之外的处理返回的单点数据上。类似的东西:(我在使用同步,而不是ReentrantLock(),它只是一个例子)
public class sampleLockClass {
private Hashtable<String, String> sampleData = new Hashtable<String, String>();
public sampleLockClass() {}
public synchronized put(String s1, String s2) {
this.sampleData().put(s1, s2);
}
public synchronized Hashtable<String, String> getAll() {
return this.sampleData; // This is returned for the processing outside the class
}
}
在这种情况下,getAll()方法返回sampleData Hashtable,因为它需要在类本身之外进行处理。原因是数据被传递给我正在利用的其他API,并且它们与这种方法不兼容。例如他们希望有一个单独的,线程安全的副本供其使用。
也许这是一个愚蠢或无问题,但你如何在需要的时间内使返回的sampleData线程安全?请注意,目前只有服务会写入sampleData。其他所有内容都是只读的,我可能会尝试通过Intent通过CommService从Activity提交对sampleData的任何更新。
尝试为每个get()方法制作Hashtable sampleData的副本会更安全吗?
答案 0 :(得分:0)
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentHashMap.html
支持检索的完全并发和可调整的哈希表 预期的更新并发性。本课程遵循相同的功能 规范为Hashtable,包括方法版本 对应于Hashtable的每种方法。
检索操作(包括get)一般不会阻塞,所以可能 与更新操作重叠(包括put和remove)。检索 反映最近完成的更新操作的结果 坚持他们的发作。对于诸如putAll和。之类的聚合操作 清除,并发检索可能仅反映插入或删除 一些条目。
更新操作之间允许的并发性由 可选的concurrencyLevel构造函数参数(默认为16),即 用作内部尺寸的提示。该表是内部的 分区以尝试允许指示的并发数 没有争用的更新。因为哈希表中的位置是 基本上是随机的,实际的并发性会有所不同。
公开集合值()
返回此地图中包含的值的Collection视图。该系列得到了支持 通过地图,对地图的更改将反映在集合中,反之亦然。 该集合支持元素删除,删除 来自此地图的相应映射,通过Iterator.remove, Collection.remove,removeAll,retainAll和clear操作。确实如此 不支持add或addAll操作。视图的迭代器是一个 “弱一致”的迭代器永远不会抛出 ConcurrentModificationException ,并保证在构造迭代器时遍历元素,并且可能 (但不保证)反映之后的任何修改 构造