问题描述
我遇到了以下代码,并指出了一些不一致之处-多线程安全代码。
Map<String,Map<String,Set<String>> clusters = new HashMap<.........>;
Map<String,Set<String>> servers = clusters.get(clusterkey);
if(servers==null){
synchronized(clusterkey){
servers = clusters.get(clusterkey);
if(servers==null){....initialize new hashmap and put...}
}
}
Set<String> users=servers.get(serverkey);
if(users==null){
synchronized(serverkey){
users=servers.get(serverkey);
if(users==null){ ... initialize new hashset and put...}
}
}
users.add(userid);
- 为什么要在clusterkey上同步地图,而不应该将其作为监视器本身在地图上?
- 最后的users.add ...是否也应该同步?
- 以线程安全的方式添加单个用户似乎是很多代码。什么是更聪明的实现?
1楼
这里只是一些观察结果:
-
->在
clusterKey
和serverKey
上clusterKey
同步可能无法达到预期的效果。 -
最好使用
ConcurrentHashMap
和ConcurrentHashSet
。
尽管没有更多背景信息,但实际上不可能回答这个问题。
似乎代码编写者希望安全地为每个clusterKey
和serverKey
创建1个映射,以便仅可以一次添加用户。
一种(可能更好)的方法是仅在clusters
映射本身上进行synchronize
,因此您很安全,因为只有一个线程可以读取和/或写入该映射。
另一种方法是使用自定义Lock
,可能一个用于读取,另一个用于写入,尽管如果一个线程正在向Map
写入而另一个线程正在从Map
中读取该确切值,则这可能再次导致不一致。
2楼
该代码看起来像是的非完整版本,有时用于惰性初始化。 阅读提供的链接,了解为什么这是一个非常糟糕的实现。
给定代码的问题是它间歇性地失败。 当有多个线程试图使用相同的键(或具有相同哈希码的键)在地图上工作时,存在竞争状态,这意味着第一个创建的地图可能会被第二个哈希图替换。
3楼
1-同步试图避免两个线程同时在该Map中创建新的Entry。
第二个必须等待,这样他的(servers==null)
也不会返回true
。
2-该users
列表似乎超出范围,但似乎不需要同步。
也许程序员知道没有重复的userId,或者他不在乎一次又一次地重置同一用户。
3- 也许吗?