我有一个我们在应用程序中使用的 HashMap。数据是在应用程序初始加载期间从数据库中填充的,然后它始终只被读取并且从不更新。会有多个线程不断读取数据。由于数据永远不会更新,我们目前不使用任何同步,只使用 HashMap。我们现在定义它的方式是:
private volatile Map<Integer, MyData> myMap = new HashMap<>();
现在我们想通过从数据库中重新填充来每天更新一次 map 中的数据。我打算做的是将数据从数据库获取到本地 map ,比如 myLocalMap
说每天午夜。一旦我将数据从 DB 加载到 myLocalMap
,我将交换 myMap
以指向它。
所以我担心的是,在我执行 myMap = myLocalMap
时,是否有可能其他正在从 myMap
读取数据的线程得到一个空的或意想不到的结果?
如果是,我将不得不同步 myMap
。对于同步,我有以下选项:
synchronized(myMap) {} OR // synchronize all map get and update operations
ConcurrentHashMap OR
Collections.synchronizedMap(myMap)
但我对使用同步犹豫不决,因为那样我也会同步所有读取。我认为每天同步一次 map 刷新过程会影响全天不断发生的所有 map 读取。这尤其糟糕,因为我的应用程序中有许多 map 是通过这种方式读取和更新的。有什么想法/意见吗?谢谢!
最佳答案
At the point where I do myMap = myLocalMap, is there a possibility that some other thread that is reading data from myMap get an empty or unexpected result?
不,没有。读写 are atomic对于引用变量,这意味着整个操作同时发生,并且在整个操作完成之前,其他线程看不到结果。因此,任何从“myMap”读取的线程都将得到旧的 myMap 或新的 myMap,但绝不会得到空的或不一致的结果。此外,在“myMap”上使用 volatile
关键字将意味着所有线程将始终知道新数据:如果 myMap 已更新,任何在之后启动的读取操作开始的更新操作将使用更新后的值。
来自 Oracle 的 Java 教程的支持文档:
- Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
- any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable
Vogella :
If a variable is declared with the volatile keyword then it is guaranteed that any thread that reads the field will see the most recently written value.
同样来自 Vogella 的同一篇文章:
The Java language specification guarantees that reading or writing a variable is an atomic operation
另见 this引用,特别是“ list 3. 使用 volatile 变量进行安全的一次性发布”,它描述了与您的场景非常相似的场景。
顺便说一句,我同意 Giovanni 关于 ConcurrentHashMap 的观点。但在您的情况下,您不需要使用 ConcurrentHashMap,因为所有更新都发生在单个事务中,而您只是调整 Map 以指向新数据。
关于java - 同步不经常更新的 hashmap 的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22562165/