在看SimpleAliasRegistry類的代碼發(fā)現(xiàn)握础,在添加別名過程中會對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的重復(fù)引用 试幽。參考資料https://cloud.tencent.com/developer/article/1497582
自己產(chǎn)生疑惑的原因是竿秆,對ConcurrentHashMap的理解不夠深入绍绘,只知道是線程安全的解決了HashMap在擴容過程造成的閉環(huán)問題没卸。并沒有深入的理解ConcurrentHashMap提供了那些原子操作戳晌。
put,remove都會對key的Hash值節(jié)點進行上鎖泞辐,保證了這兩個操作的原子性笔横。
get方法沒有上鎖竞滓,通過對node節(jié)點內(nèi)的key,value加上volatile實現(xiàn)避免臟讀吹缔。
所以這些操作在單獨來說是線程安全具有原子性的商佑,但是如果對他們進行組合操作或者多線程操作,就會出現(xiàn)線程不安全的情況厢塘。
舉個先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進行上鎖才能保證數(shù)據(jù)準確
int key = map.get("key") + 1;
map.put("key", key);
System.out.println(key);
#}
}
});
}
Thread.sleep(3000); //模擬等待執(zhí)行結(jié)束
System.out.println("------" + map.get("key") + "------");
executorService.shutdown();
}
參考資料:
【小家Spring】分享Spring中一個小巧而優(yōu)雅的類SimpleAliasRegistry源碼分析
ConcurrentHashMap 多線程并發(fā)對值+1 總數(shù)不對
ConcurrentHashMap源碼分析(JDK8版本)