多线程Java多线程原子引用分配
我有一个使用simeple HashMap实现的缓存。就像-
HashMap<String,String> cache = new HashMap<String,String>();
此缓存大部分时间用于从中读取值。我有另一个方法可以重新加载缓存,在这个方法中,我基本上创建了一个新的缓存,然后分配引用。据我所知,对象引用的赋值在Java中是原子的
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,其他线程将无法看到更改,但我的用例将缓存中的更改传播到其他线程并不是时间关键,它们可以在较长时间内继续使用旧缓存
你看到线程问题了吗?考虑到许多线程都是从缓存读取的,并且只是在缓存被重新加载的时候。p>
编辑- 我的主要困惑是我不必在这里使用原子引用,因为赋值操作本身就是原子的
编辑- 我明白,为了使排序正确,我应该将缓存标记为volatile。 但如果refreshCache方法被标记为synchronized,我就不必将缓存设置为volatile,因为synchronized block将负责排序和可见性
# 1 楼答案
那么CopyOnWrite呢。。收藏:
它们可以很好地匹配您的情况,并且线程安全
# 2 楼答案
如果没有适当的记忆屏障,它是不安全的
有人会认为缓存的分配(cache=newCache)会在填充缓存的步骤之后发生。但是,其他线程可能会因这些语句的重新排序而受到影响,因此赋值可能会在填充缓存之前发生。因此,可以在新缓存完全构建之前获取它,或者更糟的是,可以看到ConcurrentModificationException
您需要强制执行“发生在之前”关系以防止这种重新排序,而将缓存声明为volatile将实现这一点
# 3 楼答案
您应该将缓存标记为
volatile
虽然您注意到其他线程可能会在“很长一段时间”内继续使用过时的缓存,但您应该注意到,如果没有同步边缘,它们可能会永远继续使用过时的缓存。这可能不是人们想要的行为
优先顺序(主要是由于可读性):
AtomicReference<Map<>>
volatile
另见本question
# 4 楼答案
看起来还可以。确保不太频繁地调用
refreshCache
,或将其标记为synchronized