java - 如何在 executorCompletionService 中使用 concurrentHashMap?
问题描述
我多次搜索数据库,即使我缓存了一些结果,仍然花费了很长时间。
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 是一个根据startRing
, endRing
, 来查找数据库的函数,缓存在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);
}
}
解决方案
当我使用 concurrentHashMap 在 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
}
由于在此示例中get
并put
没有以原子方式执行,因此执行此代码的两个线程最终可能会首先在缓存中查找相同的键,然后在 db.xml 中查找相同的键。它仍然是线程安全的,但我们执行了两次数据库查找而不是一次。但这只是一个简单的示例,更复杂的缓存逻辑(例如,包括缓存失效和从缓存映射中删除的逻辑)最终不仅会浪费,而且实际上是不正确的。这完全取决于地图的使用方式以及您需要从中获得什么保证。我建议您阅读ConcurrentHashMap javadoc。看看它能保证什么,不能保证什么。
换了以后会不会跑得快?</p>
这取决于太多需要提前知道的参数。数据库将如何处理并发查询?有多少查询?单个查询有多快?等等。最好的了解方法是实际尝试一下。
附带说明一下,如果您正在寻找提高性能的方法,您可能想尝试使用批处理查询。然后流程将是在缓存中搜索您需要的所有键,收集您需要查找的键,然后在一个查询中将它们一起发送到数据库。在许多情况下,单个大型查询会比一堆较小的查询运行得更快。
此外,在您的情况下,您应该检查地图中的并发查找是否比单线程查找更快。也许仅并行化查询本身,而不是缓存查找可以在您的情况下产生更好的结果。
推荐阅读
- youtube-api - Youtube API inStream 参数为空
- javascript - 如果所有项目具有相同的 Id,如何使用单击事件删除单击的项目
- visual-studio-code - VS Code:当我关闭子文件夹并返回文件时,子文件夹会重新打开
- html - 单选按钮不会出现在网站上
- python - 我想向列表中添加一个值并用它做一些事情
- bash - bash 中的简单 'printf' 搞砸了输出
- html - Flexbox: 反向包装/“蛇形包装”
- python - 考虑三个数字,nr_1、nr_2 和 nr_3。如何编写一个程序,使 nr_1 具有最大值而 nr_3 最小
- android - AOSP 添加预建 APK - 错误未知目标
- javascript - 无法通过 javascript 在 php 服务器上获取数据;