Можно ли получить тупик с ConcurrentHashMap в этих обстоятельствах? - PullRequest
10 голосов
/ 23 марта 2019

Я читаю исходный код ConcurrentHashMap в JDK8, обратите внимание, что TreeBin использует блокировку «чтение-запись» для предотвращения одновременного чтения и записи.

Потоки чтения будут проходить через TreeNodes, если нет потока параллельной записи, пытающегося изменить древовидную структуру. Когда операция 'find' завершена, чтение ветки может:

(1) 'CAS' lockState и «отменить парковку» потока официанта (писателя), если он существует.

Ниже приведен метод 'find ()' в исходном коде.

final Node<K,V> find(int h, Object k) {
            if (k != null) {
                for (Node<K,V> e = first; e != null; ) {
                    int s; K ek;
                    if (((s = lockState) & (WAITER|WRITER)) != 0) {
                        if (e.hash == h &&
                            ((ek = e.key) == k || (ek != null && k.equals(ek))))
                            return e;
                        e = e.next;
                    }
                    else if (U.compareAndSwapInt(this, LOCKSTATE, s,
                                                 s + READER)) {
                        TreeNode<K,V> r, p;
                        try {
                            p = ((r = root) == null ? null :
                                 r.findTreeNode(h, k, null));
                        } finally {
                            Thread w;
                            // (1)if no more readers, try to unpark the waiter if it exists
                            if (U.getAndAddInt(this, LOCKSTATE, -READER) ==
                                (READER|WAITER) && (w = waiter) != null)
                                LockSupport.unpark(w);
                        }
                        return p;
                    }
                }
            }
            return null;
        }

с другой стороны, автор может:

  • (2) добавление состояния WAITER к lockState с помощью операции CAS.

  • (3) установить себе переменную waiter.

  • (4) сам «парк».

вот код автора:

        private final void contendedLock() {
            boolean waiting = false;
            for (int s;;) {
                if (((s = lockState) & ~WAITER) == 0) {
                    if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER)) {
                        if (waiting)
                            waiter = null;
                        return;
                    }
                }
                else if ((s & WAITER) == 0) {
                    if (U.compareAndSwapInt(this, LOCKSTATE, s, s | WAITER)) {
                        waiting = true;
                        waiter = Thread.currentThread();
                    }
                }
                else if (waiting)
                    LockSupport.park(this);
            }
        }

ЗДЕСЬ МОЙ СБОРНИК:

Если указанные выше четыре операции выполняются в этом порядке (2) (1) (3) (4), операция (1) ничего не будет снимать с парковки, поскольку в этот момент «официант» был нулевым.

Тогда официант навсегда припаркуется, и никто не сможет его разгрузить.

Все последующие записи будут заблокированы на внутренней блокировке, удерживаемой «припаркованным» потоком.

ЭТО ШАНС ЗАКРЫТИЯ?

Я действительно смущен этим. Я думаю, что, возможно, я что-то упустил в исходном коде. Нужна ваша помощь, если вы с ней знакомы.

...