我有一个我们在应用程序中使用的HashMap。在应用程序初始加载期间从数据库填充数据,然后它始终只是读取并且从不更新。将有多个线程不断读取数据。由于数据永远不会更新,我们目前不使用任何同步,只使用HashMap。我们现在定义的方式是:
private volatile Map<Integer, MyData> myMap = new HashMap<>();
现在,我们希望每天通过从数据库重新填充来更新地图中的数据。我计划做的是将数据从数据库获取到当地地图,myLocalMap
每天午夜说。将数据从数据库加载到myLocalMap
后,我只需将myMap
交换为指向此内容。
所以我担心的是,在我myMap = myLocalMap
这一点上,是否有可能从myMap
读取数据的其他线程得到空的或意外的结果?
如果是,我将必须同步myMap
。为了同步,我有以下选项:
synchronized(myMap) {} OR // synchronize all map get and update operations
ConcurrentHashMap OR
Collections.synchronizedMap(myMap)
但是我对使用同步犹豫不决,因为我也在同步所有的读取。我认为每天一次刷新同步地图将影响全天不断发生的地图读取。这是特别糟糕的,因为我的应用程序中有许多地图以这种方式读取和更新。有什么想法/评论?谢谢!
答案 0 :(得分:4)
在我做myMap = myLocalMap的时候,是否有可能 从myMap读取数据的其他一些线程得到一个空或 出人意料的结果?
不,没有。读取和写入are atomic作为参考变量,这意味着整个操作一次全部发生,结果对其他线程不可见,直到整个操作完成。因此,任何从'myMap&#39;将得到旧的myMap或新的myMap,但永远不会得到空的或不一致的结果。此外,在&#39; myMap&#39;上使用volatile
关键字这意味着所有线程将始终知道新数据:如果myMap已更新,则在更新操作开始后启动的任何读取操作将使用该更新值。
来自Oracle Java教程的支持文档:
- 读取和写入对于引用变量和大多数原始变量(除long和double之外的所有类型)都是原子的。
- 对volatile变量的任何写入都会建立与之后读取相同变量
的先发生关系
如果使用volatile关键字声明变量,那么它就是 保证读取该字段的任何线程都会看到最多 最近的书面价值。
同样来自Vogella的同一篇文章:
Java语言规范保证读取或写入 变量是原子操作
另见this引用,特别是&#34;清单3.使用volatile变量进行安全的一次性发布&#34;它描述了一个与你非常相似的场景。
顺便说一句,我同意Giovanni对ConcurrentHashMap的看法。但在您的情况下,您不需要使用ConcurrentHashMap,因为所有更新都发生在单个事务中,而您只是将Map调整为指向新数据。
答案 1 :(得分:3)
使用ConcurrentHashMap
。来自javadoc:
[...]检索操作不需要锁定[...]
编辑:根据评论,由于您的地图实际上是不可变的,因此您也可以使用Guava's ImmutableMap
:
private volatile Map<K,V> map = ImmutableMap.of();
上面创建了一个空的不可变地图,只读和线程安全(客户无法意外修改它,风险为{{1} } S)。
当您需要从数据库重新填充地图时,您只需构建一个新地图:
ConcurrentModificationException
正如@KyleM所指出的那样,将地图声明为volatile意味着上述作业将为atomic,即一旦完成,所有客户都将看到新地图。因此,这应该通过两个简单的步骤解决所有并发问题。
请注意,此模式有点类似于将不可变集合存储在Scala中的ImmutableMap.Builder<K,V> builder = ImmutableMap.builder();
// add your k,v pairs to the builder
builder.put(foo,bar);
// now swap the map with the new one:
map = builder.build();
中。
作为旁注,您应该查看Guava's caches:它们可能会为您解决更多问题。
答案 2 :(得分:0)
我同意Giovanni,除非你想在更新哈希时锁定读取?然后,您可能需要进行一些自定义同步。
我猜你不希望在更新中发生读取,但我不确定。
答案 3 :(得分:0)
如果您只是使用已初始化的地图和volatile关键字交换属性myMap,则不需要任何类型的同步
如果您需要定期更新某些值,ConcurrentHashMap方法会很好。