<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have searched the <a href="https://github.com/apache/dubbo/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have checked the <a href="https://github.com/apache/dubbo/blob/master/FAQ.md">FAQ</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Dubbo version: 2.7.7 及其之前的版本</li>
</ul>
<h3 dir="auto">Steps to reproduce this issue</h3>
<ol dir="auto">
<li>贴出源码</li>
</ol>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="private final ConcurrentMap&lt;String, Object&gt; locks = new ConcurrentHashMap&lt;&gt;();"><pre class="notranslate"><code class="notranslate">private final ConcurrentMap&lt;String, Object&gt; locks = new ConcurrentHashMap&lt;&gt;();
</code></pre></div>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="    private List&lt;ReferenceCountExchangeClient&gt; getSharedClient(URL url, int connectNum) {
        String key = url.getAddress();
        // 略去无关代码

        // 【1】
        locks.putIfAbsent(key, new Object());

       // 【2】
        synchronized (locks.get(key)) {
            clients = referenceClientMap.get(key);
            if (checkClientCanUse(clients)) {
                batchClientRefIncr(clients);
                return clients;
            }
            connectNum = Math.max(connectNum, 1);
            if (CollectionUtils.isEmpty(clients)) {
                clients = buildReferenceCountExchangeClientList(url, connectNum);
                referenceClientMap.put(key, clients);
            } else {
                for (int i = 0; i &lt; clients.size(); i++) {
                    ReferenceCountExchangeClient referenceCountExchangeClient = clients.get(i);
                    if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) {
                        clients.set(i, buildReferenceCountExchangeClient(url));
                        continue;
                    }
                    referenceCountExchangeClient.incrementAndGetCount();
                }
            }

            // 【3】
            locks.remove(key);
            return clients;
        }
    }
"><pre class="notranslate"><code class="notranslate">    private List&lt;ReferenceCountExchangeClient&gt; getSharedClient(URL url, int connectNum) {
        String key = url.getAddress();
        // 略去无关代码

        // 【1】
        locks.putIfAbsent(key, new Object());

       // 【2】
        synchronized (locks.get(key)) {
            clients = referenceClientMap.get(key);
            if (checkClientCanUse(clients)) {
                batchClientRefIncr(clients);
                return clients;
            }
            connectNum = Math.max(connectNum, 1);
            if (CollectionUtils.isEmpty(clients)) {
                clients = buildReferenceCountExchangeClientList(url, connectNum);
                referenceClientMap.put(key, clients);
            } else {
                for (int i = 0; i &lt; clients.size(); i++) {
                    ReferenceCountExchangeClient referenceCountExchangeClient = clients.get(i);
                    if (referenceCountExchangeClient == null || referenceCountExchangeClient.isClosed()) {
                        clients.set(i, buildReferenceCountExchangeClient(url));
                        continue;
                    }
                    referenceCountExchangeClient.incrementAndGetCount();
                }
            }

            // 【3】
            locks.remove(key);
            return clients;
        }
    }

</code></pre></div>
<ol start="2" dir="auto">
<li>问题描述：</li>
</ol>
<ul dir="auto">
<li>注意上述 【1】、【2】、【3】处代码</li>
<li>假设这种情况：并发环境下，线程 A、B 持有相同的 key，执行到该方法时。如果 A 执行到 同步块中，并在 <code class="notranslate">locks.remove(key)</code> 真正 remove 之前，线程 B 刚好执行到 <code class="notranslate">locks.putIfAbsent(key, new Object())</code>。</li>
<li>当 B 执行完该语句后，在 B 执行 <code class="notranslate">locks.get(key)</code> 之前，正好 A remove 完成。</li>
<li>此时，B 执行到 <code class="notranslate">locks.get(key)</code> 将拿到 null，此时 抛出 NPE</li>
</ul>
<ol start="3" dir="auto">
<li>问题提出：</li>
</ol>
<ul dir="auto">
<li>查看过历史 issue 后，使用 ConcurrentHashMap 解决了历史版本中 key.intern() 潜在性的死锁问题，但是随之也可能会有上述的 NPE 问题，所以这是被允许的吗?</li>
</ul>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have searched the <a href="https://github.com/apache/dubbo/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/apache/dubbo/blob/master/FAQ.md">FAQ</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Dubbo version: xxx</li>
<li>Operating System version: xxx</li>
<li>Java version: xxx</li>
</ul>
<h3 dir="auto">Steps to reproduce this issue</h3>
<ol dir="auto">
<li>xxx</li>
<li>xxx</li>
<li>xxx</li>
</ol>
<p dir="auto">Pls. provide [GitHub address] to reproduce this issue.</p>
<h3 dir="auto">Expected Result</h3>
<p dir="auto">What do you expected from the above steps？</p>
<h3 dir="auto">Actual Result</h3>
<p dir="auto">What actually happens?</p>
<p dir="auto">If there is an exception, please attach the exception trace:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Just put your stack trace here!"><pre class="notranslate"><code class="notranslate">Just put your stack trace here!
</code></pre></div>