Инструкция по переупорядочению ConcurrentHashMap? - PullRequest
15 голосов
/ 15 февраля 2011

Я смотрю на реализацию ConcurrentHashMap, и меня что-то смущает.

/* Specialized implementations of map methods */

        V get(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)) {
                        V v = e.value;
                        if (v != null)
                            return v;
                        return readValueUnderLock(e); // recheck
                    }
                    e = e.next;
                }
            }
            return null;
        }

и

    /**
     * Reads value field of an entry under lock. Called if value
     * field ever appears to be null. This is possible only if a
     * compiler happens to reorder a HashEntry initialization with
     * its table assignment, which is legal under memory model
     * but is not known to ever occur.
     */
    V readValueUnderLock(HashEntry<K,V> e) {
        lock();
        try {
            return e.value;
        } finally {
            unlock();
        }
    }

и конструктор HashEntry

/**
     * ConcurrentHashMap list entry. Note that this is never exported
     * out as a user-visible Map.Entry.
     *
     * Because the value field is volatile, not final, it is legal wrt
     * the Java Memory Model for an unsynchronized reader to see null
     * instead of initial value when read via a data race.  Although a
     * reordering leading to this is not likely to ever actually
     * occur, the Segment.readValueUnderLock method is used as a
     * backup in case a null (pre-initialized) value is ever seen in
     * an unsynchronized access method.
     */
    static final class HashEntry<K,V> {
    final K key;
            final int hash;
            volatile V value;
            final HashEntry<K,V> next;

            HashEntry(K key, int hash, HashEntry<K,V> next, V value) {
                this.key = key;
                this.hash = hash;
                this.next = next;
                this.value = value;
            }

putреализовать

tab[index] = new HashEntry<K,V>(key, hash, first, value);

Я запутался в комментарии HashEntry, так как JSR-133 , когда HashEntry создан, все последние поля будут видны всем другим потокам, значение поле является изменчивым, поэтому я думаю, что оно видимо и другим потокам ???,Другой вопрос, является ли он следующим порядком: ссылка на объект HashEntry может быть назначена вкладке [...] до того, как она будет полностью построена (поэтому в результате другие потоки смогут увидеть эту запись, но значение e.value может быть нулевым)?

Обновление: Я прочитал эту статью, и это хорошо.Но нужно ли мне заботиться о таком случае

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

thread1:

Person p=new Person("name","student");        
queue.offer(new Person());

thread2:
Person p = queue.poll();

Есть ли вероятность того, что thread2 получит объект Person с незавершенной конструкцией, такой же, как HashEntry, на вкладке

[index]= новый HashEntry (ключ, хэш, первое, значение);?

Ответы [ 3 ]

7 голосов
/ 28 февраля 2011

Для тех, кто заинтересован в ответе Дуга Ли на эту тему, он недавно объяснил причину readValueUnderLock

Это ответ человеку, у которого возник вопрос:

В ConcurrentHashMap получить метод не требует "readValueUnderLock", потому что гонки удаление не делает значение пустым. Значение никогда не становится нулевым на от удаления нити. это означает можно получить вернуть значение для ключа, даже если удаление поток (на тот же ключ) имеет прогрессировал до точки клонирования предыдущие части списка. это хорошо, пока это желаемое эффект.

Но это означает, что "readValueUnderLock" не требуется для новой модели памяти.

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

Верно ли мое понимание.

Ответ:

Не совсем. Вы правы, что это никогда не должен называться. Тем не менее JLS / JMM можно прочитать как не совсем запретив его называть из-за недостатков в обязательных упорядочение отношений между финалами против летучие наборы в конструкторах (ключ конечное значение изменчиво), относительно читает по темам используя запись объекты. (В JMM-ese, заказ ограничения на финал выходят за пределы отношение синхронизируется с) В этом и состоит проблема с комментариями (вставлено ниже) относится к. Ни у кого нет когда-либо думал о любой практической лазейке что процессор / компилятор может найти для получения нулевого значения читать, и это может быть доказано, что ничего не существует (и возможно когда-нибудь пересмотр JLS / JMM заполню пробелы, чтобы уточнить это), но Билл Пью однажды предложил нам поставить это в любом случае только ради быть консервативно педантично правильный. Оглядываясь назад, я не такой конечно, это была хорошая идея, так как заставляет людей придумывать экзотику теории.

Все это можно посмотреть здесь

1 голос
/ 15 февраля 2011

Я запутался в комментарии HashEntry, так как JSR-133, когда-то HashEntry построены, все последние поля будут видна всем остальным потокам, значение поле изменчиво, так что я думаю видны другим темам тоже ??? .

Другие потоки также увидят значение, но ... Назначение записи (в Object []) выполняется после инициализации И в состоянии блокировки . Поэтому, если какой-либо поток увидит null, он попытается прочитать значение под блокировкой.

Другой момент, это переупорядочение, которое он сказал is: ссылка на объект HashEntry может быть назначается на вкладку [...] до ее заполнения построен (так что результат другой темы могут видеть эту запись, но e.value может быть нулевым)?

Нет, это не может быть, поскольку b / c существует изменчивое назначение (value), и это означает, что все остальные операции должны быть заданы заранее (т.е. не переупорядочены). Также имейте в виду, что создание Java-объекта является двухфазным: создание пустого объекта с полями zero / null (например, с использованием c-tor по умолчанию) и последующий вызов метода <init> (который является конструктором). Объект не может быть назначен чему-либо до завершения вызова конструктора и его последнего назначения value (для обеспечения надлежащего упорядочения, также известного как случай до)

1 голос
/ 15 февраля 2011

Насколько я понимаю модель памяти, запись в энергозависимую переменную гарантированно будет видимой при всех последующих (как определено порядке синхронизации ) чтениях этой переменной.

Однако ничто не гарантирует, что чтение e.value в get() будет следовать за записью value в конструкторе (поскольку между этими действиями нет синхронизированных с отношений), так что Модель памяти допускает такой вид переупорядочения, и в случае значения null необходима явная синхронизация, чтобы обеспечить правильное считывание значения.

ОБНОВЛЕНИЕ: Новая модель памяти гарантирует, что любая запись в энергонезависимую переменную до записи в энергозависимую переменную будет видна другим потокам после последующего чтения этой энергозависимой переменной, , но не наоборот .

Вот соответствующий отрывок из Модель памяти Java Джереми Мэнсона, Уильяма Пью и Сариты Адве :

5.1.1 Укрупнение блокировки.
...
Все это просто окольный способ сказать, что обращается к нормальным переменным может быть переупорядочено с последующим изменчивым чтением или захватом блокировки, или с предшествующим энергозависимая запись или блокировка разблокировки. Это подразумевает, что нормальный доступ может быть перемещен внутри областей блокировки, но (по большей части) не из них;

Следовательно, присвоение построенного объекта может быть переупорядочено с помощью записи в переменную volatile внутри конструктора, так что требуется проверка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...