Fork/join實現Excel批量導入去重

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();
    }
}
複製代碼
相關文章
相關標籤/搜索