![](/img/trans.png)
[英]How to properly add elements to ConcurrentHashMap in multiple threads using ExecutorService
[英]How to add elements to ConcurrentHashMap using ExecutorService
我需要从每个 userId 的 2 个不同来源 (db) 读取用户信息,并将合并信息存储在 Map 中,键为 userId。 用户数量可能会根据他们选择的时间段而有所不同。 用户组可能属于不同的年限,例如每日、每周、每月用户。
我使用 HashMap 和 LinkedHashMap 来完成这项工作。 由于它减慢了进程并使其更快,我想到在这里使用线程。
现在阅读一些教程和示例,我正在使用 ConcurrentHashMap 和 ExecutorService。
在基于某些验证的情况下,我想跳过当前迭代并移至下一个用户信息。 它不允许在 for 循环中使用continue 关键字。 有什么方法可以在多线程代码中以不同的方式实现相同的目标。
此外,下面的代码片段虽然可以工作,但它并没有比没有线程的代码快得多,这会让人怀疑 Executor Service 是否正确实现。
如果我们在多线程代码中遇到任何错误,我们如何调试。 执行保持在调试点,但它不一致,它不会移动到下一行 F6 。
有人可以指出我是否在代码中遗漏了什么。 或者任何其他类似用例的例子也有很大帮助。
public void getMap() throws UserException
{
long startTime = System.currentTimeMillis();
Map<String, Map<Integer, User>> map = new ConcurrentHashMap<String, Map<Integer, User>>();
//final String key = "";
try
{
final Date todayDate = new Date();
List<String> applyPeriod = db.getPeriods(todayDate);
for (String period : applyPeriod)
{
try
{
final String key = period;
List<UserTable1> eligibleUsers = db.findAllUsers(key);
Map<Integer, User> userIdMap = new ConcurrentHashMap<Integer, User>();
ExecutorService executor = Executors.newFixedThreadPool(eligibleUsers.size());
CompletionService<User> cs = new ExecutorCompletionService<User>(executor);
int userCount=0;
for (UserTable1 eligibleUser : eligibleUsers)
{
try
{
cs.submit(
new Callable<User>()
{
public User call()
{
int userId = eligibleUser.getUserId();
List<EmployeeTable2> empData = db.findByUserId(userId);
EmployeeTable2 emp = null;
if (null != empData && !empData.isEmpty())
{
emp = empData.get(0);
}else{
String errorMsg = "No record found for given User ID in emp table";
logger.error(errorMsg);
//continue;
// conitnue does not work here.
}
User user = new User();
user.setUserId(userId);
user.setFullName(emp.getFullName());
return user;
}
}
);
userCount++;
}
catch(Exception ex)
{
String errorMsg = "Error while creating map :" + ex.getMessage();
logger.error(errorMsg);
}
}
for (int i = 0; i < userCount ; i++ ) {
try {
User user = cs.take().get();
if (user != null) {
userIdMap.put(user.getUserId(), user);
}
} catch (ExecutionException e) {
} catch (InterruptedException e) {
}
}
executor.shutdown();
map.put(key, userIdMap);
}
catch(Exception ex)
{
String errorMsg = "Error while creating map :" + ex.getMessage();
logger.error(errorMsg);
}
}
}
catch(Exception ex){
String errorMsg = "Error while creating map :" + ex.getMessage();
logger.error(errorMsg);
}
logger.info("Size of Map : " + map.size());
Set<String> periods = map.keySet();
logger.info("Size of periods : " + periods.size());
for(String period :periods)
{
Map<Integer, User> mapOfuserIds = map.get(period);
Set<Integer> userIds = mapOfuserIds.keySet();
logger.info("Size of Set : " + userIds.size());
for(Integer userId : userIds){
User inf = mapOfuserIds.get(userId);
logger.info("User Id : " + inf.getUserId());
}
}
long endTime = System.currentTimeMillis();
long timeTaken = (endTime - startTime);
logger.info("All threads are completed in " + timeTaken + " milisecond");
logger.info("******END******");
}
您真的不想创建一个线程池,该线程池的线程数与您从数据库中读取的用户数一样多。 这在大多数情况下没有意义,因为您需要记住线程需要在某处运行......没有多少服务器为您的应用程序保留 10 或 100 甚至 1000 个内核。 根据您的环境,一个小得多的值(例如 5)通常就足够了。
与关于性能的话题一样:您首先需要测试您的实际瓶颈是什么。 您的应用程序可能根本无法从线程中受益,因为例如您正在读取一个 db ,它同时只允许 5 个并发连接。 在这种情况下,所有其他 995 线程将只是等待。
需要考虑的另一件事是网络延迟:从多个线程读取多个用户 ID 甚至可能会增加从数据库中获取一个用户数据所需的往返时间。 另一种方法可能不是一次读取一个用户,而是一次读取所有 10'000 个用户的数据。 这样,您与数据库的可能可用的 10 GBit 以太网连接可能会真正加快速度,因为您与数据库的通信开销很小,但它可能会在一个答案中快速为您提供所需的所有数据。
简而言之,在我看来,您的问题通常是关于您的问题的性能优化,但您还不够了解,无法决定走哪条路。
你可以尝试这样的事情:
List<String> periods = db.getPeriods(todayDate);
Map<String, Map<Integer, User>> hm = new HashMap<>();
periods.parallelStream().forEach(s -> {
eligibleUsers = // getEligibleUsers();
hm.put(s, eligibleUsers.parallelStream().collect(
Collectors.toMap(UserTable1::getId,createUserForId(UserTable1:getId))
});
); //
在 createUserForId 你做你的数据库阅读
private User createUserForId(Integer id){
db.findByUserId(id);
//...
User user = new User();
user.setUserId(userId);
user.setFullName(emp.getFullName());
return user;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.