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