You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ConcurrentLinkedHashMap.Builder的删除与get处理及删除后get的线程安全问题

Understanding ConcurrentLinkedHashMap's Get and Delete Behavior in Multi-Threaded LRU Caches

Great question! Let's dive into how ConcurrentLinkedHashMap.Builder handles delete and get operations, especially the race condition you're worried about in your code.

First, Key Thread-Safety Basics

ConcurrentLinkedHashMap is built on top of ConcurrentHashMap, so all its core operations (get, remove, containsKey) are thread-safe and atomic—no extra synchronization is needed from your side. But there's a catch when you combine containsKey and get like you did, which we'll cover next.

The Race Condition in Your Code

In your getEntity method:

if (entityLRUCache.containsKey(entityId)) {
    // Another thread could delete entityId here!
    Entity entity = entityLRUCache.get(entityId);
    // entity might be null now
}

The problem is that containsKey and get are two separate operations. Even if containsKey returns true, another thread can delete the key in the split second between those two calls. When you then call get, it will return null—a classic race condition.

How Delete and Get Operations Work Internally

Let's break down what happens under the hood:

  • Delete (remove) operation: This is an atomic action that removes the entry from both the underlying concurrent hash table and the LRU linked list. The operation uses CAS (Compare-and-Swap) and lock-free mechanisms to ensure thread safety without blocking other operations. Once the delete completes, no subsequent get calls will find the key.
  • Get operation: This is also atomic. It checks the hash table for the key:
    • If found, it updates the LRU linked list (moving the entry to the head to mark it as recently used) and returns the value.
    • If not found, it returns null immediately.

Crucially, get and remove operations don't block each other. A get might briefly see a stale value if a remove is in progress (due to ConcurrentHashMap's weak consistency), but once the remove finishes, all subsequent gets will correctly return null.

Fixing Your Code

The simplest way to eliminate the race condition is to replace the containsKey + get pair with a single get call:

Entity getEntity(GUID entityId) {
    Entity entity = entityLRUCache.get(entityId);
    if (entity != null) {
        // Handle existing entity
    } else {
        // Handle missing entity (maybe load it from DB, etc.)
    }
}

This works because get is atomic—there's no window for another thread to delete the key between checking existence and retrieving the value.

Additional Notes for LRU Cache Usage

  • When you delete a key, it's fully removed from the LRU eviction pool, so it won't interfere with future eviction decisions.
  • If you need to perform a conditional delete (e.g., only delete if the value matches a certain state), use remove(Object key, Object value) which is also atomic.

内容的提问来源于stack exchange,提问作者Alex Kokorin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:22:13