Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Почему ConcurrentHashMap#containsValue()


Автор: Royan 29.7.2008, 15:07
Задаю вопрос по человечески. Обнаружил, что аналигичные по своему смыслу методы в совершенно разных по смыслу классах реализованы по разному. Пример:

PriorityBlockingQueue.contains(Object):
Код

    public boolean contains(Object o) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return q.contains(o);
        } finally {
            lock.unlock();
        }
    }


ConcurrentHashMap.Segment.containsKey(Object)
     
Код

        boolean containsKey(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry<K,V> e = getFirst(hash);
                while (e != null) {
                    if (e.hash == hash && key.equals(e.key))
                        return true;
                    e = e.next;
                }
            }
            return false;
        }


Не вдаваясь в детали реализации сразу видно, что методы не меняющие состояния объекта (containsKey, containsValue, get) в ConcurrentHashMap лишины какой бы то нибыло синхронизации, в то время как в PriorityBlockingQueue аналогичные (не меняющие состояния очереди)  методы (size(), contains()) синхронизированы. Вопрос почему?

Автор: Platon 29.7.2008, 15:39
Я думаю, это связано с внутренней реализацией PriorityQueue
Там оочень важна синхронизация, т.к. есть возможность получить ArrayIndexOutOfBoundsException

Автор: yaja 29.7.2008, 16:02
Стоит почитать java-doc к этим классам. ConcurrentHashMap не гарантирует синхронность данных. Если операция чтения совпадает с операцией изменения структуры, то ты эти изменения можешь не увидеть. 
Гарантируется только безопасность работы со структурой из нескольких потоков, чего нету у стандартных HashMap'ов. 

Автор: Royan 29.7.2008, 17:41
Цитата(yaja @  29.7.2008,  13:02 Найти цитируемый пост)
ConcurrentHashMap не гарантирует синхронность данных.[...]Гарантируется только безопасность работы со структурой из нескольких потоков

Я не совсем понял, что вы имеете ввиду под синхронностью данных, которая не гарантируется?

Цитата(Platon @  29.7.2008,  12:39 Найти цитируемый пост)
Там оочень важна синхронизация

С доводом не соглашусь, по той причине что и там и там гарантируется безопасность работы в многопоточной среде. Другое дело что для ConcurrentHashMap задокументировано, что операции чтения не синхронизированы: 
Цитата

Retrievals reflect the results of the most recently completed update operations holding upon their onset

К тому же в ConcurrentHashMap были предприняты шаги для того чтобы (даже не знаю как это назвать) увеличить вероятность чтения актуальной информации. О чем это я? Из коментариев к классу java.util.concurrent.ConcurrentHashMap.Segment<K, V>
Цитата

- All (unsynchronized) read operations must first read the "count" field, and should not look at table entries if it is 0.


Поле count объявлено вот так:
Код

transient volatile int count;

Напомню значение модификатора volatile (цитата из JLS3.0)
Цитата

A field may be declared volatile, in which case the Java memory model (§17) ensures that all threads see a consistent value for the variable.

Теперь, внимание вопрос: Почему аналогичным образом не поступить во всех наследниках AbstractQueue?

Автор: COVD 29.7.2008, 18:30
Цитата

Не вдаваясь в детали реализации сразу видно, что методы не меняющие состояния объекта (containsKey, containsValue, get) в ConcurrentHashMap лишины какой бы то нибыло синхронизации, в то время как в PriorityBlockingQueue аналогичные (не меняющие состояния очереди)  методы (size(), contains()) синхронизированы. Вопрос почему?


Royan, вы сами и ответили на свой вопрос - реализовано по-разному потому, что это "разные по смыслу классы". Один - очередь на основе связанного списка. Другой - мап, т.е. массив. 

Автор: ivg 29.7.2008, 18:53
Короче суть в том, что методы записи ConcurrentHashMap, возможно вкупе с методами чтения, реализованы таким образом, что в любой момент переключения потоков, логическая целостность объекта ConcurrentHashMap, как такового(имеется ввиду непротиворечивость состояния внутренних полей), гарантируется. В отличии от объекта класса PriorityQueue, для которого PriorityBlockingQueue является просто синхронизированной обёрткой.

Автор: Royan 29.7.2008, 19:02
Цитата(COVD @  29.7.2008,  15:30 Найти цитируемый пост)
Один - очередь на основе связанного списка. Другой - мап, т.е. массив. 

Я не нахожу ответа на вопрос, почему если мап то можно кое-что не синхронизировать, а если очередь (не важно на какой основе, ибо все что отростает от AbstractQueue синхронизировано вдоль и поперек) так вот если очередь то, как я говорил, надо ее насквозь залочить.

Автор: COVD 29.7.2008, 20:58
Цитата

Я не нахожу ответа на вопрос, почему если мап то можно кое-что не синхронизировать, а если очередь (не важно на какой основе, ибо все что отростает от AbstractQueue синхронизировано вдоль и поперек) так вот если очередь то, как я говорил, надо ее насквозь залочить.


Ну, наверное, потому, что методы, изменяющие обьект, по-разному меняют структуру. Например, при удалении элемента в мапе значение массива в соответствующем индексе просто становится равно null. А в связанном списке при удалении обнуляются ссылки на следующие элементы. Последствия при итерации возможно будут разные, если не синхронизировать. Или еще что-то. Это следствие оптимизации классов с учетом нюансов структуры. 


Автор: Royan 30.7.2008, 15:56
Цитата(COVD @  29.7.2008,  17:58 Найти цитируемый пост)
Последствия при итерации возможно будут разные, если не синхронизировать.

Похоже на то 


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)