java与scala,异步执行的比较
近期项目需实现针对批量数据进行相关计算并返回数据的操作。具体实现为:
- 从缓存中读取批量数据;
- 针对每条数据进行相关计算;
- 计算结果再进行相关操作,比如过滤,求和等;
- 批量结果返回。
客户端等待返回数据进行展示。这样就要求每次计算批量数据时,速度够快,毫秒内响应。否则客户端就处于长期等待的状态。
在实现时,比较了java与scala的实现方式,得出使用scala实现更简洁的结论。
首先,每条数据的实现逻辑是相同的,计算,过滤,求和,然后得出结果,显然这个操作是同步的,但是不同数据之间需要进行异步计算,如果使用java实现,那么我们的实现逻辑就是:
private List<Response> thread(List<String> data) throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
CountDownLatch countDownLatch = new CountDownLatch(data.size());
List<Optional<String>> resultList = new ArrayList<>();
data.forEach(a -> {
executorService.execute(() -> {
// 计算
String result = calculate(a);
resultList.add(Optional.ofNullable(result));
});
countDownLatch.countDown();
});
countDownLatch.await();
List<Response> responselist = resultList.stream()
.filter(b -> b.isPresent())
.map(d -> d.get())
.map(c -> sum(c))
.collect(Collectors.toList());
return responselist;
}
首先先用线程进行异步计算,将计算结果用列表返回,全部计算结果返回后,再进行后面的过滤,求和等其他操作,最后返回结果给客户端。似乎这是这类异步处理问题的常规操作。但是,这时候我们只保证了数据计算的异步处理,只有所有数据计算完成之后(返回list),才能进行后续的过滤等操作。这样看来,虽然因为异步节省了数据计算的时间,但是后续的处理仍是同步的,当然也可以用异步计算的相同操作来做后面的处理。但是还是需要在全部结果返回后才能进行操作。
而用scala,scala中的Future操作,就可以进行完全异步处理。scala中的Future一旦构建完成,控制权就会立刻返回给调用者,但结果值却无法立刻保证可用。这句话的意思是,Future操作指向最终可用的结果,在future操作执行完毕之前,可以继续执行其他的操作。
所以针对这个功能,我们就可以在计算完一个直接到后续的过滤求和的操作,当所有数据计算完成后,后续的过滤以及求和的操作也都完成了,具体可以这样实现:
private def calculate(data: List[String]): List[AdResponse] = {
val d =
data.map(a => Future {
if (a.isEmpty) {
Option.empty
} else {
Some(new AdResponse(a))
}
})
val m = Future
.sequence(d)
.map(a => a.filter(a => a.isDefined).map(a => a.get))
Await.result(m, 10.second)
}
以上操作可以在每条数据计算完成之后,立刻进行下面的操作,当所有数据计算完成,计算结果也就完成了,可以直接返回。
java与scala比较后,java还是要在所有计算结果完成后再继续下一步,就会多出等待所有计算结果完成的时间,而scala可以节省出这个时间,保证所有的操作都是异步进行。
所以当我们在工作或学习中遇到这样类似的实现时,可以考虑使用scala进行代码编写,代码简洁且处理方便,并可保证低延时。