背景
在批量接口调用时,我们不会选择循环遍历的串行调用方式,因为响应的时间会是所有调用时间之和,如查100个数据,每次查询50毫秒,循环调用串行的总时间时100*50=5000毫秒。
一般会选择使用批量查询接口或多线程来解决上述问题。但多线程由于并行性,受网络波动影响,会出现后执行的先得到返回结果的情况,在某些需要保序的场景,就需要关注结果的顺序和请求的顺序是否一致了。
举例
针对业务场景不同,举例两种方式:
-
使用List<Future<T>>保存线程池执行返回的Future。获取结果时,由于list是有序的,所以遍历List<Future<T>>得到的结果也是有序的,下面简单举例:
ExecutorService executor=....;//线程池 List<Integer> idList=...;//待查询的id List<someDto> resultList=new ArrayList();//存放查询的结果 List<Future<someDto>> futureList=new ArrayList();//存放线程池查询的Future //此时,线程池是按idList的顺序遍历的,所以idList中顺序和futureList中顺序一致 idList.forEach(id->{ futureList.add(executor.submit(()->{ return someService.queryById(id); })); }); //此时是按照futureList的顺序遍历,所以resultList中的顺序和futureList顺序一致,也就和入参idList的顺序一致了 futureList.forEach(future->{ resultList.add(future.get()); });
-
使用Stream编程,stream使用CompletableFuture 实现多线程的使用,简单举例如下:
ExecutorService executor=....;//线程池 List<Integer> idList=...;//待查询的id List<someDto> resultList=new ArrayList();//存放查询的结果 List<someDto> sortedList=new ArrayList();//存放保序后的结果 //这里从idList.stream()...开始看,allOf是从最后的结果集取值。 //这里先遍历每个id,并执行someService.queryById获取结果,并使用thenAccept指定得到结果后,将结果放入resultList中。 //使用map将CompletableFuture.supplyAsync的返回结果集转为数组(toArray()),CompletableFuture.allOf的入参是数组类型。 //最终使用get()获取所有的值,此时由于多线程响应的快慢不一样,所以顺序已经乱了。 CompletableFuture.allOf(idList.stream() .map(id -> CompletableFuture.supplyAsync(() -> someService.queryById(id), executorService).thenAccept(resultList::add)) .toArray(CompletableFuture[]::new)) .get(); //这里将乱序的结果resultList,转换成Map,其中key是id,value是每个子项自己。 Map<Integer,someDto>map=resultList.stream().collect(Collector.toMap(someDto::getId,dto->dto)); //遍历入参idList,按照id从上述map中逐个get值,以达到保序的功能 idList.forEach(id->{ sortedList.add(map.get(id)); });
比较
不考虑性能,总体来看,stream的多线程用法似乎比传统的Future用法要复杂。但是如果业务场景不是简单的多线程使用,常规用法解决不了问题,拿第一个例子举例:
ExecutorService executor=....;//线程池
List<Integer> idList=...;//待查询的id
......
//*************注意这里*************
//此处添加一个批量操作,先对idList做一次封装,封装后再用封装的结果执行多线程,如果这里调用的批量服务不能保序,如搜索,数仓等接口,用其默认的排序。
List<midDto> midList=midService.batchWrap(idList);
//再对midList做多线程操作,此时,多线程futureList中的顺序和批量操作返回的顺序相同,不再和idList一致
......
此时,最终还是需要对结果集做一次保序处理。
大家有其他思路吗?欢迎留言分享一下~