我有一个嵌套的集合在一个映射内的映射,我试图同步两个线程。
映射实例化如下:
private final Map<Manageable, Map<String, Set<Manageable>>> manageableMap =
Collections.synchronizedMap(new HashMap<Manageable, Map<String, Set<Manageable>>>());
这是我用来给map添加值的函数:
private void put(Manageable key, Manageable value, String valueType) {
synchronized (manageableMap) {
Map<String, Set<Manageable>> setMap = manageableMap.get(key);
if (setMap == null) {
setMap = new HashMap<String, Set<Manageable>>();
manageableMap.put(key, Collections.synchronizedMap(setMap));
}
synchronized (setMap) {
Set<Manageable> set = setMap.get(valueType);
if (set == null) {
set = new HashSet<Manageable>();
setMap.put(valueType, Collections.synchronizedSet(set));
}
synchronized (set) {
set.add(value);
}
}
}
}
Intellij IDEA警告我,我正在同步本地变量setMap和set。
我对同步相当陌生,我想知道这是否是正确的方法像这样同步一个嵌套的数据结构
谢谢你的帮助
您只需要在manageableMap上同步。一次只有一个线程可以获得manageableMap上的锁,所以如果一个线程已经获得了manageableMap上的锁,就不需要进一步锁定set和setMap,因为只有一个线程(锁定了manageableMap的线程)可以访问set和setMap。
同步作用于特定的对象实例,而不是保存对它们的引用的字段或变量,因此您必须确保同步发生在相同的对象实例上,无论它们是否仅由局部变量引用。
在这种特殊情况下,IDEA不能静态地检查代码是否做了你想要的,警告只是某种"代码气味",警告你效果可能不像预期的那样。
最好的办法是你通过注销你正在同步的实例来测试你的代码,并检查它们是否是你所期望的——IDEA也有在调试期间标记对象实例的选项,以检查你同步的对象是相同的还是不同的实例。
如果您总是手动控制同步,则可能不需要使用Collections.synchronizedMap。在目标映射上同步一个区段就可以了。