与以下链接相同,我使用与提问者相同的代码 Java multi-threading atomic reference assignment 在我的代码中,有
HashMap<String,String> cache = new HashMap<String,String>();
public class myClass {
private HashMap<String,String> cache = null;
public void init() {
refreshCache();
}
// this method can be called occasionally to update the cache.
//Only one threading will get to this code.
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
}
//Many threads will run this code
public void getCache(Object key) {
ob = cache.get(key)
//do something
}
}
我一遍又一遍地阅读了sjlee的答案,我无法理解这些代码在哪种情况下会出错。有人能给我举个例子吗? 请记住,我不关心getCache函数是否会获取旧数据 对不起,我无法对上述问题发表评论,因为我的声誉不高。 所以我只想添加一个新问题。
答案 0 :(得分:1)
如果没有记忆障碍,您可能会看到null
或旧地图,但您可能会看到不完整的地图。即你看到它的一些但不是全部。因此,如果您不介意条目丢失但是您可能会看到Map对象而不是它所引用的任何内容导致可能的NPE,那么这不是问题。
无法保证您会看到完整的地图。
final
字段将可见,但非最终字段可能不会显示。
答案 1 :(得分:0)
这是一个非常有趣的问题,它表明您的核心假设之一
”“请记住,我不在乎
getCache
函数会变老 数据。”
不正确。
我们认为,如果“ refreshCache
”和“ getCache
”不同步,那么我们只会得到旧数据,这是不正确的。
由初始线程进行的调用可能永远不会反映在其他线程中。由于缓存不是易失性的,因此每个线程都可以自由地保留它自己的本地副本,而永远不会使其在各个线程之间保持一致。
由于多线程的“ visibility
”方面,即除非我们使用适当的锁定或使用volatile
,否则我们不会触发先发生的情况,这种情况会迫使线程进行在运行它们的多个处理器之间具有一致的共享变量值,这意味着“ cache
”可能永远不会初始化,从而在getCache
中引起明显的NPE
要正确理解这一点,我建议阅读“ Java并发实践”一书中的16.2.4节,该节处理了双重检查的锁定代码中的类似问题。
解决方案:应该
refreshCache
同步生效,所有线程只要有一个线程调用它,便更新其HashMap副本,或者cache
不稳定或refreshCache
的每个线程中调用getCache
,这违背了公共缓存的目的。