我有一个使用simeple HashMap实现的缓存。喜欢 -
HashMap<String,String> cache = new HashMap<String,String>();
此缓存大部分时间用于从中读取值。我有另一个方法重新加载缓存,在这个方法内部我基本上创建一个新的缓存,然后分配引用。据我所知,对象引用的赋值是Java中的Atomic。
public class myClass {
private HashMap<String,String> cache = null;
public void init() {
refreshCache();
}
// this method can be called occasionally to update the cache.
public void refreshCache() {
HashMap<String,String> newcache = new HashMap<String,String>();
// code to fill up the new cache
// and then finally
cache = newcache; //assign the old cache to the new one in Atomic way
}
}
据我所知,如果我没有将缓存声明为volatile,其他线程将无法看到更改,但对于我的用例来说,将缓存中的更改传播到其他线程并不是时间关键,并且它们可以继续工作使用旧缓存延长时间。
您是否看到任何线程问题?考虑到许多线程正在从缓存中读取,并且有时只重新加载缓存。
编辑 - 我的主要困惑是我不必在这里使用AtomicReference,因为赋值操作本身就是原子的?
编辑 - 我理解为了使顺序正确,我应该将缓存标记为volatile。 但是如果refreshCache方法被标记为synchronized,我不必将缓存设置为volatile,因为Synchronized块将负责排序以及可见性吗?
答案 0 :(得分:27)
没有适当的内存屏障,不安全。
有人会认为在填充缓存的步骤之后会发生缓存(cache = newCache)的分配。但是,其他线程可能会遭受这些语句的重新排序,因此分配可能在填充缓存之前发生。因此,可以在完全构造之前获取新缓存,甚至可以看到ConcurrentModificationException。
您需要强制执行before-before关系以防止此重新排序,并将缓存声明为volatile将实现此目的。
答案 1 :(得分:4)
您应将缓存标记为volatile
。
虽然您注意到其他线程可能会“长时间”继续使用陈旧缓存,但您应该注意到,如果没有同步边缘,它们可能会永远继续使用陈旧缓存。这可能不是理想的行为。
按优先顺序排序(主要是由于可读性):
AtomicReference<Map<>>
volatile
另见question。
答案 2 :(得分:0)
CopyOnWrite ..集合怎么样:
java.util.concurrent.CopyOnWriteArraySet
java.util.concurrent.CopyOnWriteArraySet
and org.apache.mina.util.CopyOnWriteMap
它们在您的情况下可以很好地匹配,并且它们是线程安全的。
答案 3 :(得分:-1)
似乎没问题。请确保refreshCache
未被过频率调用或标记为synchronized
。