每一个线程分别查询出数据,添加到同一个集合中,实现分线程查询,经测,速度提升明显。
线程数量最好写死,不要动态生成,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);
}