同步嵌套映射和集合(Java)



我有一个嵌套的集合在一个映射内的映射,我试图同步两个线程。

映射实例化如下:

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。在目标映射上同步一个区段就可以了。

最新更新