我的set在ConcurrentMap中是否是线程安全的?

时间:2012-02-16 14:50:18

标签: java concurrency thread-safety

我有以下代码。即使集合本身不是线程安全的,它是否是线程安全的?

private ConcurrentMap<REGISTRY, Set<CONTACT_ROLES>> proxyRoles = new ConcurrentHashMap<REGISTRY, Set<CONTACT_ROLES>>(); 


    public void setProxyRoles(ConcurrentMap<REGISTRY, Set<CONTACT_ROLES>> proxyRoles) {
        this.proxyRoles = proxyRoles;
    }

    public ConcurrentMap<REGISTRY, Set<CONTACT_ROLES>> getProxyRoles() {
        return proxyRoles;
    }

    public synchronized void addProxyRole(REGISTRY reg, CONTACT_ROLES role) {
        if(proxyRoles.get(reg) == null){
            proxyRoles.put(reg, new HashSet<CONTACT_ROLES>());
        }
        proxyRoles.get(reg).add(role);

    }

修改

经过一些非常好的答案后,我明白我的解决方案不安全,我用google搜索了一下,在ConcurrentSkipListSet中找到了一个很好的替换

4 个答案:

答案 0 :(得分:3)

来自Javadoc:

  

支持检索的完全并发和可调整的哈希表   预期的更新并发性。本课程遵循相同的功能   规范为Hashtable,包括方法版本   对应于Hashtable的每种方法。 然而,尽管如此   操作是线程安全的,检索操作不需要   锁定,并没有任何支持锁定整个表   一种阻止所有访问的方式。

因此,如果2个线程同时访问该集合,它们将不会获得整个集合的锁定。

答案 1 :(得分:2)

不,Set在锁之外是可变的,变异的和可访问的。使用不可变Set s。

会好得多
public void addProxyRole(REGISTRY reg, CONTACT_ROLES role) {
    Set<CONTACT_ROLES> old =
        proxyRoles.putIfAbsent(reg, Collections.singleton(role));
    if (old == null) {
        return;
    }

    for (;;) {
        Set<CONTACT_ROLES> set = new HashSet<>(old);
        set.add(role);

        if (proxyRoles.replace(reg, old, Collections.unmodifiableSet(set))) {
            return;
        }
        old = proxyRoles.get(reg);
    }
}

(免责声明:这是Stack Overflow,未编译或测试。)

循环的替代方法是使用线程安全的Set,但这也需要更加谨慎使用(例如,你不能只是迭代它)。

public void addProxyRole(REGISTRY reg, CONTACT_ROLES role) {
    Set<CONTACT_ROLES> set = proxyRoles.get(reg);
    if (set == null) {
        Set<CONTACT_ROLES> newSet = Collections.synchronizedSet(new HashSet<>());
        Set<CONTACT_ROLES> old = proxyRoles.putIfAbsent(reg, newSet);
        set = old==null ? newSet : old;
    }
    set.add(role);
}

比如说CopyOnWriteArraySet也是可能的。

答案 2 :(得分:1)

没有

主题1:

for(CONTACT_ROLES role : getProxyRoles().get(REGISTRY)){
//long running iteration
}

主题2:

getProxyRoles().get(REGISTRY).add(null);

这将导致线程1中的ConcurrentModificationException

答案 3 :(得分:0)

NO。我认为你需要重构代码/ API。或者可以通过复制http://code.google.com/p/guava-libraries来返回为不可变的集合。