怎么用Fork/Join框架实现对购物车的性能优化
购物车是电商项目中最基本的功能之一,一般会展示商品的基本信息(商品名称,图片,价格等),目前正在参加哪些活动,库存是否足够等信息,最后往往也会对当前选中的商品进行价格汇总,给用户个参考。好像并不复杂,一个商品信息的计算加上服务之间的网络延时等等算50ms,也挺短的。但是如果购物车里面有100个商品呢(这对大部分人来说不可思议,但我真见过购物车里面加了80多个商品的牛人)?5s的时间对业务来说明显是不可接受的,这时候我们就需要对购物车进行优化了。
由于购物车结果计算耗时是随着商品的增加而线性增加的,因此我们采用“分治”的思想来进行购物车的性能优化,将商品分成多份交给给多个线程来同时完成。而在Java中Fork/Join框架正好是用来解决这种问题的。
Fork/Join简介
Fork/Join是Java7提供的一个用于并行执行的任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。顾名思义,Fork即拆分任务,Join用来将子任务的结果合并
使用Fork/Join主要会用到两个类:ForkJoinTask和ForkJoinPool。
ForkJoinTask 用来创建一个ForkJoin任务。它提供在任务中执行fork()和join()操作的机制,并且会维护一个双端队列用来存储子任务。通常情况下我们不直接继承ForkJoinTask,而是继承它的两个子类RecursiveAction和RecursiveTask,其中,RecursiveTask用于有返回结果的任务,我们对购物车的优化就需要继承该类。
ForkJoinPool ForkJoinTask需要使用ForkJoinPool来执行,ForkJoinPool会从ForkJoinTask中工作队列中取出任务并执行,默认最多会创建CPU核心数的线程来执行ForkJoinTask中的任务
代码实现
直接贴代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
public class ForkJoinDemo {
public static class ItemInfoTask extends RecursiveTask<Object>{
// 商品ID集合
private List<String> list;
// 最终计算的商品大小
private final int taskSize = 1;
ItemInfoTask(List<String> list){
this.list = list;
}
@SuppressWarnings("unchecked")
@Override
protected Object compute() {
// 计算出来的结果
List<Object> result = new ArrayList<>();
// 将商品数量分成4个
if(list.size() <= taskSize){
// 假设每个商品信息的计算会耗费100ms
try {
Thread.sleep(taskSize * 50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 将计算的结果加到集合中
result.add(Collections.singletonList(new Object()));
}else{
// 对于大于 taskSize 的集合分成两半再进行并行计算
int middle = list.size() / 2;
ItemInfoTask leftTask = new ItemInfoTask(list.subList(0,middle));
ItemInfoTask rightTask = new ItemInfoTask(list.subList(middle,list.size()));
// 执行子任务
leftTask.fork();
rightTask.fork();
// 等待子任务完成,并得到执行结果
List<Object> leftResult = (List<Object>) leftTask.join();
List<Object> rightResult = (List<Object>) rightTask.join();
result.addAll(leftResult);
result.addAll(rightResult);
}
// 返回本次任务的结果
return result;
}
}
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 100; i++) {
list.add("商品ID");
}
long begin = System.nanoTime();
// 将任务放到线程池中执行
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask result = forkJoinPool.submit(new ItemInfoTask(list));
try {
// 获取结果
List x = (List) result.get();
System.out.println(x.size());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
System.out.println("花费时间:" + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - begin));
forkJoinPool.shutdown();
}
}
执行结果:
花费时间:808
总结
代码中我们假设每个商品的计算耗时为100ms,如果不使用任务分割的方式,我们将耗时10s才能计算完成,但是通过将任务拆成小任务,只使用了800ms(结果会因计算机性能不同而有差异,我的电脑是8核处理器,最多会有8个线程同时计算任务)。
本文介绍如何利用Java中的Fork/Join框架优化购物车计算性能,通过将商品计算任务分解并行处理,显著减少计算总耗时。
8

被折叠的 条评论
为什么被折叠?



