Как ConcurrentHashMap.get () предотвращает грязное чтение? - PullRequest
3 голосов
/ 18 февраля 2020

Я смотрю на исходный код ConcurrentHashMap и удивляюсь, как метод get() работает без какого-либо монитора, вот код:

public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) { 
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek))) // mark here for possible dirty read
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek)))) // mark here for possible dirty read
                    return e.val;
            }
        }
        return null;
    }

Две отмеченные мной строки делают одно и то же : проверка, соответствует ли key текущего Node<K, V> необходимому key. Если true, вернет соответствующее значение. Но что, если другой поток обрежет до return и remove() этот узел из структуры данных. Поскольку локальная переменная e по-прежнему содержит ссылку на удаленный узел, G C оставит его как есть, а метод get() по-прежнему вернет удаленное значение, что приведет к неправильному чтению.

Я что-то пропустил?

1 Ответ

3 голосов
/ 18 февраля 2020

Не :

Операции получения (включая get) обычно не блокируются, поэтому могут перекрываться с операциями обновления (включая put и * 1008). *). Извлечения отражают результаты самых последних завершенных операций обновления, выполненных с момента их появления. (Более формально, операция обновления для данного ключа имеет отношение случай-до с любым (не нулевым) поиском для этого ключа, сообщающего обновленное значение.)

Это обычно это не проблема, поскольку get никогда не вернет результат, который не мог бы произойти, если метод get получил блокировку, блокирующую операцию обновления в другом потоке. Вы просто получаете результат, как если бы вызов get произошел до начала операции обновления.

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

...