变量的同步和本地副本

我正在查看一些具有以下习语的遗留代码:

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 )检索的,而不是在方法中创建的,因此可以从其他线程访问。