Команда ConcurrentHashMap для переупорядочения?

Я рассматриваю реализацию 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;
            }

поставить реализацию

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

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

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

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

thread1:

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

thread2:
Person p = queue.poll();

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

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

Ответы

Ответ 1

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

Это ответ в ответ на вопрос:

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

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

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

Я правильно понимаю.

Ответ:

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

Можно просмотреть все здесь

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

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

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