1

我在实现一种线程安全的方式来集中更新和访问我的 Android 应用程序中的类对象列表时遇到了一些麻烦。这是一个开放式问题,无法提供很多源代码。

基本上我有一个带有活动和服务的应用程序。Service实现了UDP广播任务和UDP广播监听任务。广播的信息本质上是代表设备的类的 JSON 序列化副本。例如 UUID、IP、一些管理信息,如用户设置名称、描述等... JSON 存储在 UDP 数据包中并发送,UDP 数据包被接收并反序列化和处理。该反序列化的类存储在 Hashtable 中,当前实例属于服务。例如,每个想要访问数据的人都必须通过服务。整个事情是非常异步的。

Activity 绑定到服务(通过和扩展 Binder),因此它可以调用服务方法,例如启动/停止 UDP 任务。当收到任何更新或新设备数据时,Activity 将侦听 Service 将发出的 Android Intent,并且 UI 将显示与接收到的 UDP 数据包数据相关的信息。再次注意,数据包数据存储在属于服务的容器类中。

问题是我想不出一种合适的方法来使接收数据线程的哈希表安全。当从 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 会更安全吗?

4

1 回答 1

0

http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ConcurrentHashMap.html

支持检索的完全并发和可调整的预期更新并发的哈希表。此类遵循与 Hashtable 相同的功能规范,并包含与 Hashtable 的每个方法对应的方法版本。

检索操作(包括 get)一般不会阻塞,因此可能与更新操作(包括 put 和 remove)重叠。检索反映了最近完成的更新操作在其开始时保持的结果。对于 putAll 和 clear 等聚合操作,并发检索可能仅反映插入或删除某些条目。

更新操作之间允许的并发性由可选的 concurrencyLevel 构造函数参数(默认为 16)指导,该参数用作内部大小调整的提示。该表在内部进行了分区,以尝试允许指定数量的并发更新而不会发生争用。因为哈希表中的放置本质上是随机的,所以实际的并发性会有所不同。

公共集合值()

返回此映射中包含的值的集合视图。集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。该集合支持元素移除,即通过 Iterator.remove、Collection.remove、removeAll、retainAll 和 clear 操作从此映射中移除相应的映射。它不支持 add 或 addAll 操作。视图的迭代器是一个“弱一致”的迭代器,它永远不会抛出 ConcurrentModificationException,并保证在构造迭代器时遍历元素,并且可能(但不保证)反映构造后的任何修改。

于 2013-07-17T16:40:07.260 回答