1.概述java
最近工做中,有须要使用Fork/join 优化 的业务,本文我已实际案例出发,代码落地的形式来实现优化。数据库
Fork/Join 是什么?apache
Fork/Join框架是一个实现了ExecutorService接口的多线程处理器。它能够把一个大的任务划分为若干个小的任务并发执行,充分利用可用的资源,进而提升应用的执行效率。bash
2. 业务逻辑多线程
后台Excel批量导入学生,导入的时候须要与数据库中的数据进行比较,判断username是否重复,若是重复则不导入。(ps:实际业务中多是其余数据进行去重,逻辑是同样的,本文中用学生数据做为案例)并发
3.已知app
数据库 student表 10w条数据框架
excel中 1w条数据dom
3.思路ide
3.1 初版本思路
为了减小数据库的压力,咱们
第一步把全部数据读取到Java内存中
第二步 excel 数据 自己去重
第三步 遍历excel数据,每个username拿去与dbData判断是否有相等的
在 数据库10w excel 1w 条的数据状况下 筛选耗时 8049ms
3.2 第二步思路(加入Fork/join)
结合业务,个人思路是选择在上述的第三步优化,把以前的 1w条数据筛选 拆分成2个 5000的任务 同时进行
这样在其余因素不变的状况下 ,第三步筛选的效率会提升大概一倍
实现
第一步 编写一个有返回值的任务类,定义好
THRESHOLD_NUM (单个线程处理数据个数)
start ,end (下标)
第二步 在 compute 实现 里面实现逻辑与任务的拆分
当处理数据小于等于 单个线程处理的数据值 ,则进行去重的业务
当处理数据小于等于 单个线程处理的数据值 ,则须要对任务的拆分与结果集的合并(ps:递归调用)
第三步业务层调用
在 数据库10w excel 1w 条的数据状况下 筛选耗时 4319ms
效率提升接近一倍
4.代码
package com.itbbs.service;
import com.itbbs.ArrayListUtil;
import com.itbbs.DataUtil;
import com.itbbs.pojo.Student;
import com.itbbs.task.DistinctTask;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;
/**
* 学生操做service
* tjx
*/
public class StudentService {
/**
* 批量导入学生
*/
public void importStudent(List<Student> dbData,List<Student> excelData){
// excel自身去重
excelData = excelData.stream()
.filter(ArrayListUtil.distinctByKey(Student::getUsername)) //根据username 去重
.collect(Collectors.toList());
//不重复数据
List<Student> repetitionData = new ArrayList<>();
long s = System.currentTimeMillis();
//遍历因此excel数据
for (Student data : excelData) {
String username = data.getUsername();
//判断是否存在于 dbData中
if (!ArrayListUtil.isInclude(dbData, username)) {
//若是不存在则添加到不重复集合中
repetitionData.add(data);
}
}
long e = System.currentTimeMillis();
System.out.println("筛选耗时:"+(e-s)+"ms");
//在数据库不重复的数据里面 筛选出不重复数据
repetitionData =repetitionData.stream()
.sorted(Comparator.comparing(Student::getUsername))//根据username 排序
.collect(Collectors.toList());
// repetitionData.forEach(p-> System.out.println(p.getUsername()));
}
/**
* 批量导入学生
*/
public void importStudent2(List<Student> dbData,List<Student> excelData){
// 自身去重
excelData = excelData.stream()
.filter(ArrayListUtil.distinctByKey(Student::getUsername)) //根据username 去重
.collect(Collectors.toList());
long s = System.currentTimeMillis();
//获取不重复数据
ForkJoinPool fjp = new ForkJoinPool();
DistinctTask task = new DistinctTask(0,excelData.size(),dbData,excelData);
List<Student> repetitionData = fjp.invoke(task);
long e = System.currentTimeMillis();
System.out.println("筛选耗时:"+(e-s)+"ms");
//在数据库不重复的数据里面 筛选出不重复数据
repetitionData =repetitionData.stream()
.sorted(Comparator.comparing(Student::getUsername))//根据username 排序
.collect(Collectors.toList());
// repetitionData.forEach(p-> System.out.println(p.getUsername()));
}
public static void main(String[] args) {
// 模拟获取数据库
List<Student> dbData = DataUtil.getDbData();
// 模拟获取excel数据
List<Student> excelData = DataUtil.getExcelData();
new StudentService().importStudent(dbData,excelData);
new StudentService().importStudent2(dbData,excelData);
}
}
复制代码
package com.itbbs.task;
import com.itbbs.ArrayListUtil;
import com.itbbs.pojo.Student;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveTask;
public class DistinctTask extends RecursiveTask<List<Student>> {
//单个任务处理数据
private static final int THRESHOLD_NUM = 5000;
//下标
private int start, end;
//须要处理的数据
private List<Student> dbData;
private List<Student> excelData;
public DistinctTask(int start, int end, List<Student> dbData, List<Student> excelData) {
this.start = start;
this.end = end;
this.dbData = dbData;
this.excelData = excelData;
}
@Override
protected List<Student> compute() {
//获取当前下标下的数据
excelData = excelData.subList(start,end);
//获取须要计算的数据量
int size = excelData.size();
if(size<=THRESHOLD_NUM){
//计算
List<Student> repetitionData = new ArrayList<>();
//遍历因此excel数据
for (Student data : excelData) {
String username = data.getUsername();
//判断是否存在于 dbData中
if (!ArrayListUtil.isInclude(dbData, username)) {
//若是不存在则添加到不重复集合中
repetitionData.add(data);
}
}
return repetitionData;
}else{
//拆分
int middle = (start + end) / 2;
DistinctTask left = new DistinctTask(start,middle,dbData,excelData);
DistinctTask right = new DistinctTask(middle+1,end,dbData,excelData);
//执行子任务
left.fork();
right.fork();
//获取子任务结果
//join() 方法会阻塞到结果算出来
List<Student> lResult = left.join();
List<Student> rResult = right.join();
//何并结果
lResult.addAll(rResult);
return lResult;
}
}
}
复制代码
package com.itbbs;
import com.itbbs.pojo.Student;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 数据源 工具类
* tjx
*/
public class DataUtil {
/**
* 模拟 获取数据库
* @return
*/
public static List<Student> getDbData(){
List<Student> result = new ArrayList<Student>();
Random random = new Random();
for (int i = 0; i <100000 ; i++) {
Student student = new Student();
student.setUsername(random.nextInt(99)+"");
result.add(student);
}
return result;
}
/**
* 模拟 获取excel数据
* @return
*/
public static List<Student> getExcelData(){
List<Student> result = new ArrayList<Student>();
Random random = new Random();
for (int i = 0; i <10000 ; i++) {
Student student = new Student();
student.setUsername(random.nextInt(100000)+"");
result.add(student);
}
return result;
}
}
复制代码
package com.itbbs;
import com.itbbs.pojo.Student;
import org.apache.commons.lang3.ArrayUtils;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
public class ArrayListUtil {
/** * 去重复元素 * @param keyExtractor * @param <T> * @return */ public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> map = new ConcurrentHashMap<>(); return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
/**
* 判断 list 是否包含 targetValue
* @param list
* @param targetValue
* @return
*/
public static boolean isInclude(List<Student> list, String targetValue){
return ArrayUtils.contains(list.toArray(),targetValue);
}
}
复制代码
package com.itbbs.pojo;
public class Student {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Student) {
Student student = (Student) obj;
return (username.equals(student.username));
}
return super.equals(obj);
}
@Override
public int hashCode() {
Student student = (Student) this;
return student.username.hashCode();
}
}
复制代码