我正在查看一些具有以下习语的遗留代码:
Map<String, Boolean> myMap = someGlobalInstance.getMap();
synchronized (myMap) {
item = myMap.get(myKey);
}
我从Intelli-J的代码检查得到的警告是:
Synchronization on local variable 'myMap'
这是适当的同步吗?为什么?
Map<String, Boolean> myMap = someGlobalInstance.getMap();
synchronized (someGlobalInstance.getMap()) {
item = myMap.get(myKey);
}
答案 0 :(得分:12)
这被标记为问题的原因是因为同步局部变量通常是一个坏主意。
如果 someGlobalInstance.getMap()
返回的对象总是相同的,那么synchronized块确实使用了这个准全局对象监视器,代码产生了预期的结果。
如果您只需要同步get()
/ put()
个调用并且没有任何更大的同步块,我也同意使用同步包装器的建议。但请确保通过包装器访问地图仅 ,否则您将有另一次机会发现错误。
另请注意,如果someGlobalInstance.getMap()
不一直返回相同的对象,那么即使您的第二个代码示例也无法正常工作,它甚至可能比原始代码更糟糕您可以在与您调用get()
的对象不同的对象上进行同步。
答案 1 :(得分:4)
我认为代码可能是合理的,具体取决于getMap()方法的作用。如果它保持对必须在线程之间共享的实例的引用,那么它是有意义的。警告无关紧要,因为本地变量未在本地初始化。
答案 2 :(得分:2)
我认为最好将[{3}}用于地图
答案 3 :(得分:2)
Alex是正确的,因为通过调用Collections.synchronizedMap(Map)
添加同步包装器是一种典型的方法。但是,如果采用这种方法,可能仍然存在需要在Map
的锁上进行同步的情况;例如在迭代地图时。
Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<String, String>());
// Synchronized on map to prevent ConcurrentModificationException whilst iterating.
synchronized (syncMap) {
for (Map.Entry<String, String> entry : syncMap.entrySet()) {
// Do work
}
}
在您的示例中,可以忽略来自IDEA的警告,因为很明显您的本地变量:map
是从其他地方(someGlobalInstance
)检索的,而不是在方法中创建的,并且可以因此可能从其他线程访问。