繁体   English   中英

如何使用 ExecutorService 向 ConcurrentHashMap 添加元素

[英]How to add elements to ConcurrentHashMap using ExecutorService

我需要从每个 userId 的 2 个不同来源 (db) 读取用户信息,并将合并信息存储在 Map 中,键为 userId。 用户数量可能会根据他们选择的时间段而有所不同。 用户组可能属于不同的年限,例如每日、每周、每月用户。

我使用 HashMap 和 LinkedHashMap 来完成这项工作。 由于它减慢了进程并使其更快,我想到在这里使用线程。

现在阅读一些教程和示例,我正在使用 ConcurrentHashMap 和 ExecutorService。

  1. 在基于某些验证的情况下,我想跳过当前迭代并移至下一个用户信息。 它不允许在 for 循环中使用continue 关键字 有什么方法可以在多线程代码中以不同的方式实现相同的目标。

  2. 此外,下面的代码片段虽然可以工作,但它并没有比没有线程的代码快得多,这会让人怀疑 Executor Service 是否正确实现。

  3. 如果我们在多线程代码中遇到任何错误,我们如何调试。 执行保持在调试点,但它不一致,它不会移动到下一行 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM