мой набор будет потокобезопасным в ConcurrentMap? - PullRequest
3 голосов
/ 16 февраля 2012

У меня есть следующий код.Будет ли это потокобезопасным, даже если сам набор не является потокобезопасным?

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

    }

РЕДАКТИРОВАТЬ:

После некоторых очень хороших ответов я понимаю, что мое решение не будетбыть в безопасности, и я немного погуглил и нашел хорошую замену для своего набора в ConcurrentSkipListSet

Ответы [ 4 ]

3 голосов
/ 16 февраля 2012

От Javadoc:

Хеш-таблица, поддерживающая полный параллелизм получения и настраиваемый ожидаемый параллелизм для обновлений.Этот класс подчиняется той же функциональной спецификации, что и Hashtable, и включает версии методов, соответствующие каждому методу Hashtable. Однако, несмотря на то, что все операции являются поточно-ориентированными, операции извлечения не влекут за собой блокировку, и нет никакой поддержки для блокировки всей таблицы таким образом, чтобы предотвратить любой доступ.

Таким образом, если 2 потока одновременно получают доступ к набору, они не получат блокировку всего набора.

2 голосов
/ 16 февраля 2012

Нет, 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);
    }
}

(Отказ от ответственности: это переполнение стека, не скомпилировано или протестировано.)

Альтернативой циклу было бы использование потокаsafe 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);
}

A CopyOnWriteArraySet, скажем, также возможно.

1 голос
/ 16 февраля 2012

Нет:

Тема 1:

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

Тема 2:

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

Это приведет к ConcurrentModificationException в теме 1

0 голосов
/ 16 февраля 2012

NO.Я думаю, что вам нужно реорганизовать код / ​​API.Или можете вернуть как неизменную коллекцию, сделав копию http://code.google.com/p/guava-libraries.

...