java - 如何在executorCompletionService中使用concurrentHashMap?

标签 java multithreading thread-safety

我在数据库中搜索了很多次,即使我缓存了一些结果,但仍然花费了很长时间。

List<Map<Long, Node>> aNodeMapList = new ArrayList<>();
Map<String, List<Map<String, Object>>> cacheRingMap = new ConcurrentHashMap<>();
for (Ring startRing : startRings) {
    for (Ring endRing : endRings) {
        Map<String, Object> nodeMapResult = getNodeMapResult(startRing, endRing, cacheRingMap);
        Map<Long, Node> nodeMap = (Map<Long, Node>) nodeMapResult.get("nodeMap");
        if (nodeMap.size() > 0) {
            aNodeMapList.add(nodeMap);
        }
    }
}

getNodeMapResult是根据startRingendRing查找数据库的函数,并缓存在cacheRingMap中,下次可能不需要搜索数据库,如果我发现结果存在于 cacheRingMap

领导告诉我可以使用多线程技术。所以我把它改为executorCompletionService,但现在我有一个问题,当我使用concurrentHashMap在executorCompletionService中缓存结果时,这个线程安全吗? 改了之后跑得快吗?

int totalThreadCount = startRings.size() * endRings.size();
ExecutorService threadPool2 = Executors.newFixedThreadPool(totalThreadCount > 4 ? 4 : 2);
CompletionService<Map<String, Object>> completionService = new ExecutorCompletionService<Map<String, Object>>(threadPool2);
for (Ring startRing : startRings) {
    for (Ring endRing : endRings) {
        completionService.submit(new Callable<Map<String, Object>>() {
            @Override
            public Map<String, Object> call() throws Exception {
                return getNodeMapResult(startRing, endRing, cacheRingMap);
            }
        });
    }
}

for (int i = 0; i < totalThreadCount; i++) {
    Map<String, Object> nodeMapResult = completionService.take().get();
    Map<Long, Node> nodeMap = (Map<Long, Node>) nodeMapResult.get("nodeMap");
    if (nodeMap.size() > 0) {
        aNodeMapList.add(nodeMap);
    }
}

最佳答案

Is this thread safe when I use concurrentHashMap to cache result in executorCompletionService?

ConcurrentHashMap 本身是线程安全的,正如其名称所暗示的那样(“并发”)。但是,这并不意味着使用它的代码是线程安全的。

例如,如果您的代码执行以下操作:

SomeObject object = cacheRingMap.get(someKey); //get from cache
if (object == null){ //oh-oh, cache miss
    object = getObjectFromDb(someKey); //get from the db
    cacheRingMap.put(someKey, object); //put in cache for next time
}

由于在此示例中 getput 不是自动执行的,因此执行此代码的两个线程最终可能会首先在缓存中查找相同的键,然后在数据库中。它仍然是线程安全的,但我们执行了两次数据库查找,而不是一次。但这只是一个简单的例子,更复杂的缓存逻辑(比如包括缓存失效和从缓存映射中删除的逻辑)最终可能不仅是浪费,而且实际上是不正确的。这完全取决于 map 的使用方式以及您需要从中获得什么保证。我建议您阅读ConcurrentHashMap javadoc 。看看它能保证什么,不能保证什么。

Will it run fast after I change?

这取决于太多参数,无法提前了解。数据库如何处理并发查询?有多少查询?单个查询的速度有多快?等等。最好的了解方法就是实际尝试一下。

顺便说一句,如果您正在寻找提高性能的方法,您可能需要尝试使用批量查询。然后,流程将在缓存中搜索您需要的所有键,收集您需要查找的键,然后在单个查询中将它们全部发送到数据库。在许多情况下,单个大型查询比一堆较小的查询运行得更快。

此外,您应该检查映射中的并发查找是否比您的情况下的单线程查找更快。也许仅并行化查询本身而不并行化缓存查找可以在您的情况下产生更好的结果。

关于java - 如何在executorCompletionService中使用concurrentHashMap?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58398901/

相关文章:

java - Ant 与 JavaMail : authentication required

java - Android应用程序ActivityThread中的RuntimeException未能找到一些来源?

c++ - 解释双重检查锁定中的竞争条件

database - 用于保存 django 模型的单独线程

iOS Swift5 将使用带锁的 NSMutableDictionary 而不是 var [ :] ensure thread safety?

java - jdbc mysql 与 jasper 服务器的连接出错

java - Timestamp.valueOf() 根据 JVM 版本返回不同的值

java - 使用 Netty 访问 MySql 数据库

.NET 线程处于 'pre-emptive GC disabled' 模式,会阻塞 GC 并可能导致死锁

ios - Objective C - 将对象添加到数组,同时改变该对象