数据库查询数据量过大,分线程查询,代码实现

每一个线程分别查询出数据,添加到同一个集合中,实现分线程查询,经测,速度提升明显。

线程数量最好写死,不要动态生成,15到20个线程为好。 

 

    /**
     * @Description 线程查询 (怀疑遍历数据中有空值存在的问题与此线程的执行有关)
     * @Author lx
     * @Date 2019/11/22 15:27
     * @Param obj方法执行对象  methodName方法名称  argsClass参数列表  dataCount数据总量
     * @Return
     * @Exception
     */
    private List<Map<String, Object>> execThreadToSelect(Object obj, String methodName, Object[] args, int dataCount) throws InterruptedException {
        //存放所有线程返回的数据
        List<Map<String, Object>> allDataList = new ArrayList<>();
        //数据集合大小
        int listSize = dataCount;
        //开启的线程数
        int runSize = 20;
        //一个线程处理数据条数
        int count = listSize / runSize;
        if (count == 0) {
            runSize = 1;
        }
        //创建一个线程池,数量和开启线程的数量一样
        ExecutorService executor = Executors.newFixedThreadPool(runSize);
        //创建两个个计数器
        CountDownLatch begin = new CountDownLatch(1);
        CountDownLatch end = new CountDownLatch(runSize);
        //计算sql语句中每个分页查询的起始和结束数据下标
        int beginIndexSql = 0;
        int endIndexSql = 0;
        //循环创建线程
        for (int i = 0; i < runSize; i++) {
            //计算每个线程执行的数据
            if ((i + 1) == runSize) {
                beginIndexSql = (i * count);
                endIndexSql = dataCount;
            } else {
                beginIndexSql = (i * count);
                endIndexSql = (i + 1) * count - 1;
            }
            int finalBeginIndexSql = beginIndexSql;
            int finalEndIndexSql = endIndexSql;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        //加finalBeginIndexSql 和 finalEndIndexSql  给 newArgs
                        Object[] newArgs = new Object[args.length + 2];
                        for (int j = 0; j < args.length; j++) {
                            newArgs[j] = args[j];
                        }
                        newArgs[args.length] = finalBeginIndexSql;
                        newArgs[args.length + 1] = finalEndIndexSql;
                        //存放每个线程的执行数据
                        List<Map<String, Object>> selectList = (List<Map<String, Object>>) invokeMethod(obj, methodName, newArgs);
                        allDataList.addAll(selectList);
                        //执行完让线程直接进入等待
                        begin.await();
                    } catch (Exception e) {
                        log.error("线程查询失败", e);
                    } finally {
                        //当一个线程执行完 了计数要减一不然这个线程会被一直挂起
                        //end.countDown(),这个方法就是直接把计数器减一的
                        end.countDown();
                    }
                }
            });
        }
        begin.countDown();
        end.await();
        //执行完关闭线程池
        executor.shutdown();
        return allDataList;
    }

此处需要重新创建参数对象用于接收传入的参数,否则会出现并发修改,造成参数错误。

 invokeMethod反射执行:

    /**
     * @Description 执行对应方法
     * @Author lx
     * @Date 2019/11/18 15:18
     * @Param owner 方法执行者   methodName执行方法名   args参数列表
     * @Return
     * @Exception
     */
    private Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
        Class ownerClass = owner.getClass();
        Class[] argsClass = new Class[args.length];
        for (int i = 0, j = args.length; i < j; i++) {
            if (args[i] != null) {
                argsClass[i] = args[i].getClass();
            }
        }
        // 获得包含私有方法在内的所有方法
        Method method = ownerClass.getDeclaredMethod(methodName, argsClass);
        // 设置私有方法可以被访问
        method.setAccessible(true);
        return method.invoke(owner, args);
    }

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值