在CGLIB源码中寻找动态代理会重写那些方法时,看到这样一段代码:
//定义的类变量
private static volatile Map<ClassLoader, ClassLoaderData> CACHE = new WeakHashMap<ClassLoader, ClassLoaderData>();
//--------------代码片段-------------------------
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
//volatile+synchronized的双重检测
if (data == null) {
//这部分不是很明白为什么要重新建立一个map后,再put进去,替换掉原来的map
//为什么要这么写,发现很多这种利用map做缓存都是这样子 例如sentinel的NodeSelectorSlot 记录当前context和资源的DefaultNode
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
volatile保证了CACHE变量的可见性- 双重检测+
synchronized保证了CACHEput是线程安全的,对应的data也会只有一份(单例模式)
这是Sentinel框架中NodeSelectorSlot代码片段,也是用Map记录一些信息,那时并没有在意双重检测后为什么要新建Map后加入,然后替换掉原来的map。但是今天在找CGLIB生成代理类是支持那些方法时发现它在用map做单例缓存时(也就是上面的代码)也是这么做的!!!所以就很好奇为什么要这样写(单例模式的双重检测我能理解)这么写考虑因素是什么?直接put不行吗?涉及到知识的盲区了
//以context和字段为维度的统计节点
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);
//--------------------------------代码片段---------------------------------------
DefaultNode node = map.get(context.getName());
if (node == null) {
synchronized (this) {
node = map.get(context.getName());
if (node == null) {
node = new DefaultNode(resourceWrapper, null);
//也是新建 在复制进去
HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
cacheMap.putAll(map);
cacheMap.put(context.getName(), node);
map = cacheMap;
// Build invocation tree
((DefaultNode) context.getLastNode()).addChild(node);
}
}
}
求助各位掘金大佬
- 为什么要重新创建map?