Hashtable、ConcurrentHashMap和数据可见性

3

问题: 我有一个ViewModel基类,其中有一个HashMap来存储ViewModel的属性值。然而,我遇到了一个间歇性的错误,在向该集合添加项后,从该集合中读取(在另一个线程上)会直接返回null。

private HashMap<String, Serializable> _propertyValues = new HashMap<String, Serializable>();

一个例子:当提供用户ID时,我想获取用户信息。

ViewModel将UserID连接到PropertyChanged。属性更改处理程序创建一个后台线程来拉取值。后台线程读取UserID,然后从服务器获取。

我们看到,在某些罕见情况下,后台线程从属性哈希映射中读取null(线程2),直接在提供值后(线程1)。

我的想法:我怀疑这是由于data visibility和缺乏线程安全集合所致。当审查线程安全选项时,我遇到了以下内容:

  1. Hashtable-社区认为这已经过时,并且不鼓励使用。
  2. ConcurrentHashMap-似乎我仍然可能会遇到由于它缺乏可靠的同步而导致的问题
  3. Collections.synchronizedMap(map)-似乎在使用方式上很特别,不确定它是否比Hashtable有优势

此时,我觉得Hashtable是我想要的,但需要确认。 :)

用例:

  1. 每分钟一次左右的100次读取。
  2. UI线程频繁写入。
  3. 后台线程定期读取需要准确值的数据。在UI线程写入后,后台线程立即读取。

谢谢, Trey


您还可以使用锁定功能,以便在放弃写入锁之前,其他进程无法访问信息。 - thatidiotguy
1
锁可能是您在这里的唯一选择。在您的情况下似乎发生的是,您假设该值已被写入,但读取线程实际上在写入线程之前被调用,因此该值为空。您需要一个锁来确保写入线程完成后,读取线程才能访问该值。 - Jeshurun
“特别使用”是什么意思?使用synchronizedMap(new HashMap())的优点在于,HashMap在过去十年中得到了发展,而Hashtable没有。 - Marko Topolnik
你的另一个选择可能是使用 Executors.newSingleThreadExecutor() 像一个 Queue 一样。这样,只有一个线程在执行,如果读线程在写线程之后提交,你可以保证读线程已经完成。但这可能会导致性能下降。 - Jeshurun
@MarkoTopolnik,我听到的大多是主观评论,比如在讨论这个问题时说“如果使用得当”。没有具体的东西。 - Trey
2
使用它的两种“不当”方式是直接访问封装的映射(明显的违规行为)和在没有同步的情况下进行任何类型的迭代(与Hashtable共享的陷阱)。这就是为什么我认为相比之下,Hashtable没有太多可提供的原因。 - Marko Topolnik
1个回答

4

ConcurrentHashMap很适合这个场景。它有内部同步机制来防止这些问题的发生。还有其他一些方法可能会对你有帮助,比如putIfAbsent

Collections.synchronizedMap相比,它的主要缺点是占用更大的内存空间。


1
+1 CHM 的一个重要且独特的优势是其迭代器的一致性。 - Marko Topolnik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接