-
背景
从一个数据量约十多亿的表中,捞出符合时间范围内所需的数据 -
解决方案
在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
它可能代表一个明确完成的Future,也有可能代表一个完成阶段( CompletionStage ),它支持在计算完成以后触发一些函数或执行某些动作。
它实现了Future和CompletionStage接口
根据id分组取模,多线程分段查询,最后进行归并
创建一个并行supplier工具类,方便调用或以后复用
public class ParallelSupplierUtils {
/** 线程池 */
private static final ThreadPoolTaskExecutor EXECUTOR = new VisiableThreadPoolTaskExecutor();
static {
// 配置核心线程数
EXECUTOR.setCorePoolSize(16);
// 配置最大线程数
EXECUTOR.setMaxPoolSize(160);
// 配置队列大小
EXECUTOR.setQueueCapacity(5000);
// 配置线程池中的线程的名称前缀
EXECUTOR.setThreadNamePrefix("parallel_supplier_utils-");
// 拒绝策略:不在新线程中执行任务,而是用调用者所在的线程来执行
EXECUTOR.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待任务在关机时完成--表明等待所有线程执行完
EXECUTOR.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间:30分钟后强制停止
EXECUTOR.setAwaitTerminationSeconds(60 * 30);
// 执行初始化
EXECUTOR.initialize();
log.info("初始化ParallelSupplierUtils的线程池完毕:corePoolSize={},maxPoolSize={}",
EXECUTOR.getCorePoolSize(), EXECUTOR.getMaxPoolSize());
}
@Data
public static class Holder<FIRST, SECOND, THIRD> {
private FIRST first;
private SECOND second;
private THIRD third;
}
/**
* 并行执行三个Supplier(为空请写null),并返回结果
*
* @param s1 第一个Supplier
* @param s2 第二个Supplier
* @param s3 第三个Supplier
* @param <FIRST> 第一个Supplier的返回值类型
* @param <SECOND> 第二个Supplier的返回值类型
* @param <THIRD> 第三个Supplier的返回值类型
* @return Holder:通过getXxx()进行取值
*/
public static <FIRST, SECOND, THIRD> Holder<FIRST, SECOND, THIRD> supplyAsync(Supplier<FIRST> s1, Supplier<SECOND> s2, Supplier<THIRD> s3) {
// 创建任务返回值包装对象
Holder<FIRST, SECOND, THIRD> holder = new Holder();
List<CompletableFuture> completableFutureList = new ArrayList<>(3);
// 分别让任务异步执行
if (Objects.nonNull(s1)) {
completableFutureList.add(CompletableFuture.supplyAsync(s1, EXECUTOR).thenAccept(holder::setFirst));
}
if (Objects.nonNull(s2)) {
completableFutureList.add(CompletableFuture.supplyAsync(s2, EXECUTOR).thenAccept(holder::setSecond));
}
if (Objects.nonNull(s3)) {
completableFutureList.add(CompletableFuture.supplyAsync(s3, EXECUTOR).thenAccept(holder::setThird));
}
// 等待全部任务执行完毕
CompletableFuture[] cfs = completableFutureList.toArray(new CompletableFuture[completableFutureList.size()]);
CompletableFuture.allOf(cfs).join();
// 返回结果
return holder;
}
/**
* 执行一系列相同返回值类型的Supplier型函数,并收集返回值
*
* @param supplierList Supplier型函数集合
* @param <R> Supplier型函数的返回值类型
* @return 返回值Set
*/
public static <R> Set<R> supplyAsync(List<Supplier<R>> supplierList) {
if (CollectionUtils.isEmpty(supplierList)) {
return Collections.emptySet();
}
// 分别让任务异步执行
Set<R> resultSet = new HashSet<>(supplierList.size());
List<CompletableFuture> completableFutureList = supplierList.stream().filter(Objects::nonNull).map(s ->
CompletableFuture.supplyAsync(s, EXECUTOR).thenAccept(r -> {
if (Objects.nonNull(r)) {
resultSet.add(r);
}
})
).collect(Collectors.toList());
// 等待全部任务执行完毕
CompletableFuture[] cfs = completableFutureList.toArray(new CompletableFuture[completableFutureList.size()]);
CompletableFuture.allOf(cfs).join();
// 返回结果
return resultSet;
}
/**
* 执行一系列相同返回值类型的Supplier型函数,并收集返回值
*
* @param supplierList Supplier型函数集合
* @param <R> Supplier型函数的返回值类型
* @return 结果
*/
public static <R> List<R> supplyAsyncWithListRes(List<Supplier<R>> supplierList) {
if (CollectionUtils.isEmpty(supplierList)) {
return Collections.emptyList();
}
// 分别让任务异步执行
List<R> resultList = new ArrayList<>(supplierList.size());
// 等待全部任务执行完毕
CompletableFuture[] cfs = supplierList.stream().filter(Objects::nonNull).map(s ->
CompletableFuture.supplyAsync(s, EXECUTOR).thenAccept(r -> {
if (Objects.nonNull(r)) {
resultList.add(r);
}
})
).toArray(CompletableFuture[]::new);
CompletableFuture.allOf(cfs).join();
// 返回结果
return resultList;
}
}
可根据实际情况,动态配置参与并行线程数
List<Supplier<List<T>>> supplierList = Lists.newArrayList();
for (int i = 0; i < threadNum; i++) {
int threads = threadNum;
int currentMod = i;
supplierList.add(() -> xxxMapper.selectxxx( threads, currentMod));
}
List<List<T>> rspList = ParallelSupplierUtils.supplyAsyncWithListRes(supplierList);
List<T> result = Lists.newArrayList();
rspList.forEach(result::addAll);
<select id="selectxxx" resultType="xxx">
SELECT * FROM A a
WHERE
<if test="threadsNum > 1">
AND a.id % #{threadsNum} = #{currentMod}
</if>
</select>