在这种情况下,是否有可能使用 ConcurrentHashMap 陷入死锁?

我正在阅读JDK8中的源代码,请注意使用“读写”锁来防止并发读写。ConcurrentHashMapTreeBin

如果没有并发写入线程尝试修改树结构,则读取线程将通过 TreeNodes。当“查找”操作完成后,读取线程可能会:

(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)将状态添加到“CAS”操作。WAITERlockState

  • (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) 不会取消停放任何内容,因为“waiter”在那一刻为 null。

然后服务员将永远停车,没有人可以取消停车。

后续写入操作将在“停放”线程所持有的固有锁上全部被阻塞。

这是陷入僵局的机会吗?

我对此感到非常困惑。我想也许我在源代码中遗漏了一些东西。如果您熟悉,需要您的帮助。


答案 1

这个问题已经超过一年了。但这是一个很好的谜题。答案如下:

在 (2) (1) (3) 之后,在 contendedLock() 中执行,如下所示:

if (((s = lockState) & ~WAITER) == 0)真,因为 (1) 已执行

if (U.compareAndSwapInt(this, LOCKSTATE, s, WRITER))也是真的,因为(3)是在它之前而不是之后执行的(s = lockState)

由于在执行 (3) 之前设置为 true,因此第三个 if 语句也是 true。因此设置为 null,我们退出循环。(4)从不执行。waitingwaiter

总结一下:(2)(1)(3)之后,操作(4)将永远不会执行。因此,没有死锁的机会,我们都可以继续使用ConcurrentHashMap而不会感到疑虑;-)


答案 2

推荐