我有一个使用简单的 HashMap 实现的缓存。 like -
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
}
}
我知道,如果我不将缓存声明为易失性,其他线程将无法看到更改,但对于我的用例来说,将缓存中的更改传播到其他线程并不是时间关键,它们可以继续使用旧缓存延长时间。
您看到任何线程问题吗?考虑许多线程正在从缓存中读取数据,并且仅有时会重新加载缓存。
编辑-
我的主要困惑是我不必在这里使用 AtomicReference,因为赋值操作本身是原子的?
编辑 -
我知道为了使排序正确,我应该将缓存标记为易失性。
但是如果将刷新缓存方法标记为同步,我不必将缓存设置为易失性,因为同步块将负责排序和可见性?
I have a cache which I implemented using a simeple HashMap. like -
HashMap<String,String> cache = new HashMap<String,String>();
This cache is used most of the time to read values from it. I have another method which reloads the cache and inside of this method I basically create a new cache and then assign the reference. As I understand assignment of object reference is Atomic in 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
}
}
I understand that if I do not declare cache as volatile, other threads will not be able to see the changes but it is not time critical for my use case to propagate the change in cache to other threads and they can continue to work with old cache for extended time.
Do you see any threading issue? Consider that many threads are reading from the cache and only at times the cache is reloaded.
EDIT-
My main confusion is I do not have to use AtomicReference here as the assignment operation itself is atomic?
EDIT -
I understand that to make the ordering proper, I should mark cache as volatile.
But If refreshCache method is marked as synchronized, I do not have to make cache as volatile, as Synchronized block will take care of ordering as well as visibility?
发布评论
评论(4)
如果没有适当的内存屏障,它是不安全的。
人们可能会认为缓存的分配(cache = newCache)将在填充缓存的步骤之后发生。但是,其他线程可能会受到这些语句重新排序的影响,因此分配可能看起来是在填充缓存之前发生的。因此,有可能在新的缓存完全构建之前就获取它,甚至更糟糕的是看到 ConcurrentModificationException。
您需要强制执行“happens-before”关系来防止这种重新排序,并将缓存声明为“易失性”可以实现这一点。
It is not safe without a proper memory barrier.
One would think that the assignment of cache (cache = newCache) would happen after the steps to populate the cache. However, other threads may suffer from reordering of these statements so that the assignment may appear to happen before populating the cache. Thus, it is possible to grab the new cache before it's fully constructed or even worse see a ConcurrentModificationException.
You need to enforce the happens-before relationship to prevent this reordering, and declaring the cache as volatile would achieve that.
您应该将缓存标记为
易失性
。虽然您注意到其他线程可能会继续使用过时的缓存“很长一段时间”,但您应该注意,如果没有同步边缘,它们可能会永远继续使用过时的缓存。这可能不是所需的行为。
按照优先顺序(主要是出于可读性考虑):
AtomicReference
易失性
另请参阅此 问题。
You should mark the cache as
volatile
.While you note that other threads may continue using a stale cache for "a long time" you should note that, without a synchronization edge, they are likely to continue using the stale cache forever. That's probably not the desired behavior.
In order of preference (mostly due to readability):
AtomicReference<Map<>>
volatile
See also this question.
CopyOnWrite.. 集合怎么样:
它们可以很好地匹配您的情况,并且它们是线程安全的。
What about CopyOnWrite.. collections:
They can be a good match in your case and they are thread-safe.
似乎还可以。确保不会过于频繁地调用
refreshCache
或将其标记为synchronized
。Seems to be OK. Be sure
refreshCache
is not called too frequently or mark assynchronized
.