简体   繁体   English

如何使用 ExecutorService 向 ConcurrentHashMap 添加元素

[英]How to add elements to ConcurrentHashMap using ExecutorService

I have a requirement of reading User Information from 2 different sources (db) per userId and storing consolidated information in a Map with key as userId.我需要从每个 userId 的 2 个不同来源 (db) 读取用户信息,并将合并信息存储在 Map 中,键为 userId。 Users in numbers can vary based on period they have opted for.用户数量可能会根据他们选择的时间段而有所不同。 Group of users may belong to different Period of Year.eg daily, weekly, monthly users.用户组可能属于不同的年限,例如每日、每周、每月用户。

I used HashMap and LinkedHashMap to get this done.我使用 HashMap 和 LinkedHashMap 来完成这项工作。 As it slows down the process and to make it faster, I thought of using Threading here.由于它减慢了进程并使其更快,我想到在这里使用线程。

Reading some tutorials and examples now I am using ConcurrentHashMap and ExecutorService.现在阅读一些教程和示例,我正在使用 ConcurrentHashMap 和 ExecutorService。

  1. In cases based on some validation I want to skip the current iteration and move to next User info.在基于某些验证的情况下,我想跳过当前迭代并移至下一个用户信息。 It doesnot allow to use continue keyword to use within for loop.它不允许在 for 循环中使用continue 关键字 Is there any way to achieve same differently within Multithreaded code.有什么方法可以在多线程代码中以不同的方式实现相同的目标。

  2. Moreover below code piece though it works, but its not significantly that faster than the code without threading which creates doubt if Executor Service is implemented correctly.此外,下面的代码片段虽然可以工作,但它并没有比没有线程的代码快得多,这会让人怀疑 Executor Service 是否正确实现。

  3. How do we debug in case we get any error in Multithreaded code.如果我们在多线程代码中遇到任何错误,我们如何调试。 Execution holds at debug point but its not consistent and it does not move to next line with F6 .执行保持在调试点,但它不一致,它不会移动到下一行 F6

Can someone point out if I am missing something in the code.有人可以指出我是否在代码中遗漏了什么。 Or any other example of simillar use case also can be of great help.或者任何其他类似用例的例子也有很大帮助。

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******");
        }

You really don't want to create a thread pool with as many threads as users you've read from the db.您真的不想创建一个线程池,该线程池的线程数与您从数据库中读取的用户数一样多。 That doesn't make sense most of the time because you need to keep in mind that threads need to run somewhere... There are not many servers out there with 10 or 100 or even 1000 cores reserved for your application.这在大多数情况下没有意义,因为您需要记住线程需要在某处运行......没有多少服务器为您的应用程序保留 10 或 100 甚至 1000 个内核。 A much smaller value like maybe 5 is often enough, depending on your environment.根据您的环境,一个小得多的值(例如 5)通常就足够了。

And as always for topics about performance: You first need to test what your actual bottleneck is.与关于性能的话题一样:您首先需要测试您的实际瓶颈是什么。 Your application may simply don't benefit of threading because eg you are reading form a db which only allows 5 concurrent connections a the same time.您的应用程序可能根本无法从线程中受益,因为例如您正在读取一个 db ,它同时只允许 5 个并发连接。 In that case all your other 995 threads will simply wait.在这种情况下,所有其他 995 线程将只是等待。

Some other thing to consider is network latency: Reading multiple user ids from multiple threads may even increase the round trip time needed to get the data for one user from the database.需要考虑的另一件事是网络延迟:从多个线程读取多个用户 ID 甚至可能会增加从数据库中获取一个用户数据所需的往返时间。 An alternative approach might be to not read one user at a time, but the data of all 10'000 of them at once.另一种方法可能不是一次读取一个用户,而是一次读取所有 10'000 个用户的数据。 That way your maybe available 10 GBit Ethernet connection to your database might really speed things up because you have only small communication overhead with the database but it might serve you all data you need in one answer quickly.这样,您与数据库的可能可用的 10 GBit 以太网连接可能会真正加快速度,因为您与数据库的通信开销很小,但它可能会在一个答案中快速为您提供所需的所有数据。

So in short, from my opinion your question is about performance optimization of your problem in general, but you don't know enough yet to decide which way to go.简而言之,在我看来,您的问题通常是关于您的问题的性能优化,但您还不够了解,无法决定走哪条路。

you could try something like that:你可以尝试这样的事情:

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))
  });
); // 

And in the createUserForId you do your db-reading在 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