使用多线程批量处理数据

使用多线程批量处理数据

为什么要开启多线程处理?道理很简单,如果处理一千条数据需要10ms,那么串行处理1w条就要100ms,假如开启10个线程同时并发处理,只需要约10ms,速度提升将近十倍。当然实际上不会那么简单,需要综合考虑各种因素,但是毫无疑问使用多线程能够大大减少数据处理时长。

基本思路:

  1. 定义数据组大小,将数据分组。
  2. 定义任务。
  3. 确定线程池参数,新建线程池。
  4. 使用多线程执行任务。
  5. 合并处理结果。

下面简单来一个代码示例:

private void handleDataWithThreads() {
        // 创建模拟数据
        List<Person> data = createData(10100);
        // 1.数据分组
        List<List<Person>> lists = groupData(data, 1000);
        // 2.创建任务队列
        List<Callable<List<Person>>> taskList = new ArrayList<>();
        for (List<Person> list : lists) {
            Callable<List<Person>> task = new Callable<List<Person>>() {
                @Override
                public List<Person> call() throws Exception {
                    // 此处定义对数据的处理
                    handleData(list);
                    return list;
                }
            };
            taskList.add(task);
        }
        // 3.创建线程池
        // 参数:核心线程数;最大线程数;存活时间;存活时间单位;阻塞队列
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 60,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        try {
            // 4.执行任务
            List<Future<List<Person>>> futures = executor.invokeAll(taskList);
            // 5.合并结果
            List<Person> resultList = new ArrayList<>();
            for (Future<List<Person>> future : futures) {
                resultList.addAll(future.get());
            }
            System.out.println(resultList);
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            executor.shutdown();
        }
    }
	/**
     * 数据处理
     * @param data
     */
    private void handleData(List<Person> data) {
        log.info("当前线程:{},当前处理数据:{} - {}", Thread.currentThread(), data.get(0).getName(), data.get(data.size()-1).getName());
        for (Person person : data) {
            person.setName("new " + person.getName());
            person.setAge(person.getAge() + 10);
        }
    }
    /**
     * 生成模拟数据
     */
    private List<Person> createData(int total) {
        List<Person> people = new ArrayList<>();
        for (int i = 0; i < total; i++) {
            Person person = new Person("person"+i, 18);
            people.add(person);
        }
        return people;
    }

    /**
     * 数据分组
     * @param sourceList
     * @param groupNum
     * @return
     */
    private List<List<Person>> groupData(List<Person> sourceList, int groupNum) {
        List<List<Person>> targetList = new ArrayList<>();
        int size = sourceList.size();
        int remainder = size % groupNum;
        int sum = size / groupNum;
        for (int i = 0; i<sum; i++) {
            List<Person> subList;
            subList = sourceList.subList(i * groupNum, (i + 1) * groupNum);
            targetList.add(subList);
        }
        if (remainder > 0) {
            List<Person> subList;
            subList = sourceList.subList(size - remainder, size);
            targetList.add(subList);
        }
        return targetList;
    }

打印结果:

当前线程:Thread[pool-1-thread-10,5,main],当前处理数据:person9000 - person9999
当前线程:Thread[pool-1-thread-7,5,main],当前处理数据:person6000 - person6999
当前线程:Thread[pool-1-thread-1,5,main],当前处理数据:person0 - person999
当前线程:Thread[pool-1-thread-9,5,main],当前处理数据:person8000 - person8999
当前线程:Thread[pool-1-thread-6,5,main],当前处理数据:person5000 - person5999
当前线程:Thread[pool-1-thread-4,5,main],当前处理数据:person3000 - person3999
当前线程:Thread[pool-1-thread-3,5,main],当前处理数据:person2000 - person2999
当前线程:Thread[pool-1-thread-2,5,main],当前处理数据:person1000 - person1999
当前线程:Thread[pool-1-thread-8,5,main],当前处理数据:person7000 - person7999
当前线程:Thread[pool-1-thread-5,5,main],当前处理数据:person4000 - person4999
当前线程:Thread[pool-1-thread-4,5,main],当前处理数据:person10000 - person10099
………
  1. Java并发方面的知识就不在这里写了(其实我也不是很懂哈哈哈),关于线程池的定义、线程类型的选择等,根据自己的实际情况确定。
  2. 使用demo中的方式可以确保合并后数据的顺序和原来的一致。如果没有这种需要,可以遍历创建线程对象后直接执行并将结果添加到resultList,但是注意要resultList使用线程安全的类型。
  3. 影响执行速度的原因有很多,主要原因有数据组大小,线程数,分享本人使用中的一点小心得
  • 最好能够使每个线程的执行时间大致相同,避免出现某个线程执行时间过长导致总体时间加长。

  • 单个数据处理时间长,则将每组数据量缩减;反之则增加。

  • 线程数量其实没有一个统一的公式,总体原则是IO型线程数可以大点,CPU型线程数小点。具体情况还要自己多测试一番。

  • 需要好考虑并发的情况,每一次请求都创建一个线程池容易造成阻塞导致服务瘫痪,需要做限制或者创建一个全局线程池,这样即使多个请求进来也只是会影响到需要使用该线程池的方法。

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值