我遇到了以下代码,并指出了一些不一致的信息 - 对于多线程安全代码。
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上同步地图 - 不应该在地图上作为监视器本身?
- 不应该最后一个用户。
- 这似乎是以线程安全添加单个用户的很多代码。什么是更智能的实现?
在这里只是一些观察:
- 在
String
上同步是一个非常糟糕的主意 ->在clusterKey
上同步,serverKey
可能无法按预期的方式工作。 - 最好是使用
ConcurrentHashMap
s和ConcurrentHashSet
s。
尽管没有更多上下文,但实际上不可能回答这个问题。看来代码作者想安全地每clusterKey
和serverKey
安全地创建1个映射,因此只能添加一次用户。
a(可能更好)的方法就是在clusters
本身上仅synchronize
,然后您安全,因为只有一个线程可以读取和/或写入上述地图。
另一种方法是使用自定义Lock
s,也许是一个用于阅读的方法,而另一种则用于写作,尽管如果一个线程写入Map
,而另一个线程则从中读取该值的确切值,则可能会再次导致不一致。<<<<<<<<<<<<<<<<<<<<<<
代码看起来像是通过双检查锁定习惯的版本进行的,有时用于懒惰初始化。阅读提供的链接,以了解为什么这是一个非常糟糕的实现。
给定代码的问题是它间歇性失败。当有几个线程尝试使用相同的密钥(或具有相同哈希码的键)上工作的几个线程时,就会有种族条件,这意味着首先创建的地图可能被第二个hashmap替换。
1-同步试图避免在该地图中创建一个新条目。第二个必须等待,以便他的(servers==null)
也无法返回true
。
2- users
列表似乎不超出范围,但似乎不需要同步。也许程序员知道没有重复的用户ID,或者他不在乎一次又一次地重置同一用户。
3-可能?