ConcurrentHashMap 上的 Java containsKey() 对于映射中存在的 UUID 键返回 false

itn*_*ick 1 java uuid concurrenthashmap

我正在使用类型的对象

Map<Long, Map<UUID, OperationEntry>> stepOperations = new ConcurrentHashMap<>(); 
Run Code Online (Sandbox Code Playgroud)

来控制一些操作的执行。我有一个 Kafka 消息处理程序方法,在收到有关操作状态更改的消息后,需要根据传递的 UUID 在 stepOperations 映射中查找并更新记录的状态。

UUID 类未被覆盖(标准 java.util.UUID)。

如果地图中存在密钥“3102869c-7653-4fb7-ad47-a629b5cbac90”(如屏幕截图所示),那么从 containsKey 方法获得意外结果的原因可能是什么?

这是代码中有问题的部分:

   private OperationEntry getOperationLogEntry(UUID requestId) {
        return stepOperations.values().stream()
            .filter(value -> value.containsKey(requestId))
            .map(value -> value.get(requestId))
            .findFirst()
            .orElse(null);
    }
Run Code Online (Sandbox Code Playgroud)

使用 ConcurrentHashMap 类是因为它保证支持多线程处理。传入的 Kafka 消息由由 16 个线程组成的线程池进行处理。

内部地图的存在可能是这里的潜在问题吗?

简单的测试例如

        Map<UUID, String> testMap = new java.util.concurrent.ConcurrentHashMap<>();
        UUID key = UUID.randomUUID();
        testMap.put(key, "testValue");
        if (testMap.containsKey(key)) {
            System.out.println(testMap.get(key));
        }
Run Code Online (Sandbox Code Playgroud)

按预期工作

NoD*_*und 6

在调试器中,我们可以valueHashMap. 不是ConcurrentHashMap

哈希映射

当您使用多级别地图时,您应该使用computeIfAbsent.

从您的代码来看,您可能应该:

  • 必须stepOperations确保ConcurrentMap<Long, ConcurrentMap<UUID, Whatever>>这两个级别都良好。
  • 使用computeIfAbsentstepOperations.computeIfAbsent(key, ignored -> new ConcurrentHashMap<>())
  • 确保地图本身不可从外部访问(地图可能会被污染)。

要添加新步骤:

stepOperations.computeIfAbsent(opId,  ignored -> new ConcurrentHashMap<>()))
              .put(opUUID, opWhatever)
Run Code Online (Sandbox Code Playgroud)

最后,您可能可以重写您的代码:

stepOperations
   .values()
   .stream()
   .filter(value -> value.containsKey(operationRunState.requestId))
   .map(value -> value.get(operationRunState.requestId))
   .findFirst()
Run Code Online (Sandbox Code Playgroud)

这样做:

var id = operationRunState.requestId;
stepOperations
   .values()
   .stream().map(value -> value.get(id))
   .filter(Objects::nonNull)
   .findFirst();
Run Code Online (Sandbox Code Playgroud)

两点:

  • 使用局部变量id来确保使用相同的 id
  • ConcurrentHashMap禁止 key 和 value 存在nullcontainsKey这里没有用。

  • 我正在写一个类似的答案:) - @itnick - _outer_ 映射是一个 `ConcurrentHashMap`,但它由 k/v 映射组成,其中每个值都是一个简单的 `HashMap`。外部 ConcurrentHashMap 并不是解决所有并发问题的魔咒。内部的东西也需要具有并发能力,_并且_你仍然不能只是说:哦,我使用了 CHM,所以我很好。您需要使用支持并发的原语,并正确使用它们。即使 CHM 也无法神奇地修复 `if (!map.containsKey(k)) map.put(k, v);` - 这已经被破坏了。一切事上。`putIfAbsent` 同时适用于 CHM。 (2认同)