13

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

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);
}
4

4 回答 4

12

这被标记为问题的原因是因为在局部变量上同步通常是一个坏主意。

如果返回的对象someGlobalInstance.getMap()总是相同的,那么同步块实际上确实使用了准全局对象监视器,并且代码产生了预期的结果。

get()如果您只需要同步/put()调用并且没有任何更大的同步块,我也同意使用同步包装器的建议。但请确保地图只能通过包装器访问,否则您将有另一个机会出现错误。

另请注意,如果someGlobalInstance.getMap()不是一直返回相同的对象,那么即使您的第二个代码示例也无法正常工作,它甚至可能比您的原始代码更糟糕,因为您可以在与您调用的对象不同的对象上同步get()

于 2009-11-03T22:57:40.280 回答
4

我认为代码可能是正确的,这取决于 getMap() 方法的作用。如果它保留对必须在线程之间共享的实例的引用,那么它是有意义的。该警告无关紧要,因为局部变量未在本地初始化。

于 2009-11-03T22:55:23.657 回答
2

我认为最好为您的地图使用同步包装器

于 2009-11-03T22:50:53.227 回答
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)检索到的,而不是在方法中创建的,因此可以从其他线程访问。

于 2009-11-03T23:03:16.350 回答