在看SimpleAliasRegistry类的代码发现,在添加别名过程中会对ConcurrentHashMap上锁
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
#对存储别名的map加锁
synchronized (this.aliasMap) {
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
当时不理解既然ConcurrentHashMap是线程安全的为什么还要加锁?
加锁的目的是为了防止alias和name的重复引用 。参考资料https://cloud.tencent.com/developer/article/1497582
自己产生疑惑的原因是,对ConcurrentHashMap的理解不够深入,只知道是线程安全的解决了HashMap在扩容过程造成的闭环问题。并没有深入的理解ConcurrentHashMap提供了那些原子操作。
put,remove都会对key的Hash值节点进行上锁,保证了这两个操作的原子性。
get方法没有上锁,通过对node节点内的key,value加上volatile实现避免脏读。
所以这些操作在单独来说是线程安全具有原子性的,但是如果对他们进行组合操作或者多线程操作,就会出现线程不安全的情况。
举个先get再put的例子
public static void main(String[] args) throws InterruptedException {
final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();
map.put("key", 0);
ExecutorService executorService = Executors.newFixedThreadPool(100);
for (int i = 0; i < 1000; i++) {
executorService.execute(new Runnable() {
public void run() {
# synchronized (map){ 对map进行上锁才能保证数据准确
int key = map.get("key") + 1;
map.put("key", key);
System.out.println(key);
#}
}
});
}
Thread.sleep(3000); //模拟等待执行结束
System.out.println("------" + map.get("key") + "------");
executorService.shutdown();
}
参考资料:
【小家Spring】分享Spring中一个小巧而优雅的类SimpleAliasRegistry源码分析
ConcurrentHashMap 多线程并发对值+1 总数不对
ConcurrentHashMap源码分析(JDK8版本)