我有以下代码。即使集合本身不是线程安全的,它是否是线程安全的?
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中找到了一个很好的替换
答案 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来返回为不可变的集合。