java文件对比7,一个线程读一个线程写、返回给前端进度条数据

2023-11-12


这个其实写的是有点问题的,想的是一个线程读 多个线程对比,结果写的是一个线程读一个线程对比
总归是个写的思路

controller

package com.taiyusoft.tydms.controller;

import com.alibaba.fastjson.JSONObject;
import com.taiyusoft.tydms.entity.ResponseForm;
import com.taiyusoft.tydms.service.FileComparesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;

/***
 * @Author
 * @Param
 * @return
 **/
@RestController
@RequestMapping("/fileComparesimple")
public class FileCompareController {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private FileComparesService fileComparesService;


        @RequestMapping("/selectfileComparesimple")
    public void fileComparesimple(String firstpath, String secondpath) {

        long time1 = System.currentTimeMillis();
        File file = new File(firstpath);
        if (file.isFile() || file.isDirectory()) {
            JSONObject list = fileComparesService.fileComparesimple(firstpath, secondpath);
        }
        long time2 = System.currentTimeMillis();
        int time = (int) ((time2 - time1) / 1000);
        System.out.println("执行了:" + time + "秒!");
    }

    /**
     * 进度条初始化   直接调用文件对比
     *
     * @return
     */
    @RequestMapping("/fileComparesinit")
    public ResponseForm fileComparesinit(String firstpath, String secondpath) {
        ResponseForm responseForm = new ResponseForm();
        Integer sum = 0;
        try {
            sum = fileComparesService.initPercentage(firstpath, secondpath);
            if (sum != 0) {
                File file1 = new File(firstpath);
                File file2 = new File(secondpath);
                if (file1.isFile() || file1.isDirectory() && file2.isFile() || file2.isDirectory()) {
                    JSONObject list = fileComparesService.fileComparesimple(firstpath, secondpath);
                    responseForm.setData(list);
                    responseForm.setCode("200");
                    responseForm.setMessage(ResponseForm.message.SUCCESS);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            responseForm.setCode("400");
            responseForm.setMessage(ResponseForm.message.FAIL);
        }
        return responseForm;
    }

    /**
     * 文件对比时间过长时,出现该当前文件进度条百分比
     */
    @RequestMapping("/fileComparesimplePercentage")
    public ResponseForm fileComparesimplePercentage() {
        ResponseForm responseForm = new ResponseForm();
        JSONObject jsonObject;
        try {
            jsonObject = fileComparesService.fileComparesimplePercentage();
            responseForm.setData(jsonObject);
            responseForm.setCode("200");
            responseForm.setMessage(ResponseForm.message.SUCCESS);
        } catch (Exception e) {
            responseForm.setCode("400");
            responseForm.setMessage(ResponseForm.message.FAIL);
        }

        return responseForm;
    }

    /**
     * 文件处理时间过长时,专门获取文件对比结果
     *
     * @return
     */
    @RequestMapping("/getfileCompares")
    public ResponseForm getfileCompares() {
        ResponseForm responseForm = new ResponseForm();
        JSONObject jsonObject;
        try {
            jsonObject = fileComparesService.getfileCompares();
            responseForm.setData(jsonObject);
            responseForm.setCode("200");
            responseForm.setMessage(ResponseForm.message.SUCCESS);
        } catch (Exception e) {
            responseForm.setCode("400");
            responseForm.setMessage(ResponseForm.message.FAIL);
        }
        return responseForm;
    }
}

Service

package com.taiyusoft.tydms.service;

import com.alibaba.fastjson.JSONObject;

public interface FileComparesService {
    JSONObject fileComparesimple(String firstpath, String secondpath);

    Integer initPercentage(String firstpath, String secondpath);

    JSONObject fileComparesimplePercentage();

    JSONObject getfileCompares();
}

Serviceimpl

package com.taiyusoft.tydms.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.taiyusoft.tydms.base.util.CompareThreadUtil;
import com.taiyusoft.tydms.base.util.ReadFile2;
import com.taiyusoft.tydms.entity.Fc;
import com.taiyusoft.tydms.service.FileComparesService;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

@Service
public class FileComparesServiceImpl implements FileComparesService {
    private static final Logger logger = Logger.getLogger(FileComparesServiceImpl.class);
    public static String syncCurrentName1 = null; // 进度条name
    public static String percentage1 ; // 进度条百分比
    public static  Integer percentagesum1 = null; // 文件总数
    public static int countfile;//当前文件个数
    public static final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();//全局静态变量
    private  List listoneNot1 = new ArrayList<Fc>(); //存放数据一缺失的数据
    private  List listoneNot2 = new ArrayList<Fc>(); //存放数据二缺失的数据
    private  List listoneNot3 = new ArrayList<Fc>(); //存放数据二缺失的数据

    public static boolean flag;
    private  Boolean[] scanStop = new Boolean[1]; // 检测停止标识


    @Override
    public JSONObject fileComparesimple(String firstpath, String secondpath) {
        long startTime = System.currentTimeMillis();
        int cpuCount = 4;

//        String dirName1 = "C:\\Users\\taiyu\\Desktop\\file2\\10w";
        String dirName1 = firstpath;
        File root1 = new File(dirName1);

        String dirName2 = secondpath;
        File root2 = new File(dirName2);

        listoneNot1 = new ArrayList<Fc>();
        listoneNot2 = new ArrayList<Fc>();
        listoneNot3 = new ArrayList<Fc>();
        scanStop[0] = true;
        List list = new ArrayList();
        list.add(dirName1);
        list.add(dirName2);

        logger.info("分配线程开始...");
        CountDownLatch latchs = new CountDownLatch(cpuCount + 1);
        ExecutorService exec = Executors.newCachedThreadPool();

        // 读取文件线程
        final ReadFile2 trFile1 = new ReadFile2(
                latchs, null, list,
                 dirName1, dirName2,scanStop);
        logger.info("读取文件线程启动...");
        exec.execute(new Thread() {
            @Override
            public void run() {
                trFile1.readFile();
            }
        });


        final CompareThreadUtil thDao = new CompareThreadUtil(
                latchs, null, list, listoneNot1,
                listoneNot2, listoneNot3, root1,
                root2, dirName1, dirName2,  trFile1,scanStop);
        for (int i = 0; i < cpuCount; i++) {
            System.out.println("检测线程" + i + " 启动...");
            final int currentID = i;
            exec.execute(new Thread() {
                @Override
                public void run() {
                    thDao.addFileLevelAfter(currentID,percentagesum1,countfile);
                }
            });
        }
        try {
            exec.shutdown();
            latchs.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //结束时间
        long endTime1 = System.currentTimeMillis();
        //打印
        System.out.println("对比运行时间:" + (double) (endTime1 - startTime) / 1000 + "s");
        //打印数据1缺失数据
        if (listoneNot1.size() > 0) {
            logger.info("数据1删除的文件如下");
            for (int i = 0; i < listoneNot1.size(); i++) {
                logger.info(((Fc) listoneNot1.get(0)).getPath());
            }
        } else {
            logger.info("数据1无删除的文件...");
        }

        //打印数据2缺失数据
        if (listoneNot2.size() > 0) {
            logger.info("数据2删除的文件如下");
            for (int i = 0; i < listoneNot2.size(); i++) {
                logger.info(((Fc) listoneNot2.get(i)).getPath());
            }
        } else {
            logger.info("数据2无删除文件");
        }
        //打印数据3缺失数据
        if (listoneNot3.size() > 0) {
            logger.info("两数据源之间发生修改的文件如下");
            for (int i = 0; i < listoneNot3.size(); i++) {
                logger.info(((Fc) listoneNot3.get(i)).getPath());
            }
        } else {
            logger.info("两数据源之间无发生修改的文件");
        }
        //结束时间
        long endTime = System.currentTimeMillis();
        //打印
        System.out.println("程序运行时间:" + (double) (endTime - startTime) / 1000 + "s");


        JSONObject jsonObject = new JSONObject();
        jsonObject.put("数据源1发生删除的文件",listoneNot1);
        jsonObject.put("数据源2发生删除的文件",listoneNot2);
        jsonObject.put("两数据源对比发生修改的文件",listoneNot3);
        flag=true;
        jsonObject.put("flag",flag);
        return jsonObject;
    }

    @Override
    public Integer initPercentage(String firstpath, String secondpath) {
        flag=false;
        percentagesum1=0;
        percentage1="";
        syncCurrentName1="";
        readFileInfo(new File(firstpath));
        readFileInfo(new File(secondpath));
        return percentagesum1;
    }

    @Override
    public JSONObject fileComparesimplePercentage() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("百分比",percentage1);
        jsonObject.put("当前进行到的文件",syncCurrentName1);
        jsonObject.put("flag",flag);//为true时可以取结果

        return jsonObject;
    }

    @Override
    public JSONObject getfileCompares() {

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("数据源1发生删除的文件",listoneNot1);
        jsonObject.put("数据源2发生删除的文件",listoneNot2);
        jsonObject.put("两数据源对比发生修改的文件",listoneNot3);
        return jsonObject;
    }


    private void readFileInfo(File root) {
        if (root.exists()) {
            if (root.isDirectory()) {
                File[] files = root.listFiles();
                if (files != null) {
                    for (int i = 0; i < files.length; i++) {

                        if (files[i].isFile()) {
                            percentagesum1++;
                        } else if (files[i].isDirectory()) {
                            readFileInfo(files[i]);
                        }
                        files[i] = null;
                    }
                }
                files = null;
            } else if (root.isFile()) {
                percentagesum1++;
            }
        }
    }

}

读取文件多线程工具类

package com.taiyusoft.tydms.base.util;


import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReadFile2 {
    //    private static final Logger logger = Logger.getLogger(ReadFile.class);
    private CountDownLatch latch;
    private CyclicBarrier barrier;
    private LinkedList<File> linkedList = new LinkedList();//总


    private Lock lock = new ReentrantLock();
    private Object numberLock = new Object();
    private boolean lockboo = false;
    private List<String> list;


    private File root1;
    private File root2;
    private String dirName1;
    private String dirName2;


    private Boolean[] StopIt;


    public ReadFile2(CountDownLatch latch, CyclicBarrier barrier,
                     List list,
                     String dirName1, String dirName2,
                     Boolean[] stop

    ) {
        this.latch = latch;
        this.barrier = barrier;
        this.list = list;

        this.dirName1 = dirName1;
        this.dirName2 = dirName2;

        this.StopIt = stop;

    }


    // 读取文件信息
    public void readFile() {
        try {

            readFileInfo(new File(list.get(0)));
            readFileInfo(new File(list.get(1)));

//            logger.info("读取文件线程正常停止!");
            System.out.println("读取文件线程正常停止!");
        } catch (Exception e) {

            System.out.println("错误信息!" + e);
        } finally {
            StopIt[0] = false;
            latch.countDown();
        }
    }


    private void readFileInfo(File root) {
        if (root.exists()) {
            if (root.isDirectory()) {
                File[] files = root.listFiles();
                if (files != null) {
                    for (int i = 0; i < files.length; i++) {

                        if (files[i].isFile()) {
                            addFile(files[i]);

                        } else if (files[i].isDirectory()) {
                            readFileInfo(files[i]);
                        }
                        files[i] = null;
                    }
                }
                files = null;
            } else if (root.isFile()) {
                addFile(root);
            }
        }
    }


    private void addFile(File file) {
        lock.lock();
        try {
            linkedList.addLast(file);

        } catch (Exception e) {
            System.out.println("添加文件出错:" + e);
        } finally {
            lock.unlock();
//            try {
//                if (linkedList.size() >= 10000) {
//                    synchronized (numberLock) {
//                        lockboo = true;
//                        numberLock.wait();
//                    }
//                }
//            } catch (Exception e2) {
                logger.error("锁出错:", e2);
//                System.out.println("锁出错" + e2);
//            }
        }
    }


    public void getFileInfo(File[] file) {
        lock.lock();
        try {
            file[0] = linkedList.pollFirst();

            if (lockboo && linkedList.size() <= 1000) {
                synchronized (numberLock) {
                    lockboo = false;
                    numberLock.notify();
                }
            }
        } catch (Exception e) {
//            logger.error("获取文件出错:", e);
            System.out.println("获取文件出错:" + e);
        } finally {
            lock.unlock();
        }
    }

}

对比文件多线程工具类

package com.taiyusoft.tydms.base.util;


import com.taiyusoft.tydms.entity.Fc;
import com.taiyusoft.tydms.service.impl.FileComparesServiceImpl;

import java.io.File;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CompareThreadUtil {
    private CountDownLatch latch;
    private CyclicBarrier barrier;
    private LinkedList linkedList = new LinkedList();//总
    private LinkedList<Fc> linkedList1 = new LinkedList<Fc>();//读取路径1中的所有的文件
    private LinkedList<Fc> linkedList2 = new LinkedList<Fc>();//读取路径2中的所有的文件
    private Lock lock = new ReentrantLock();
    private List listoneNot1 = new ArrayList<Fc>(); //存放数据一缺失的数据
    private List listoneNot2 = new ArrayList<Fc>(); //存放数据二缺失的数据
    private List listoneNot3 = new ArrayList<Fc>(); //存放数据二缺失的数据

    private Object numberLock = new Object();
    private boolean lockboo = false;
    private List<String> list;


    private File root1;
    private File root2;
    private String dirName1;
    private String dirName2;


    private ReadFile2 trFile1;
    private Boolean[] StopIt;


    public CompareThreadUtil(CountDownLatch latch, CyclicBarrier barrier,
                             List list,
                             List<Fc> listoneNot1, List<Fc> listoneNot2,
                             List<Fc> listoneNot3, File root1, File root2,
                             String dirName1, String dirName2, ReadFile2 trFile1, Boolean[] stop

    ) {
        this.latch = latch;
        this.barrier = barrier;

        this.list = list;
        this.listoneNot1 = listoneNot1;
        this.listoneNot2 = listoneNot2;
        this.listoneNot3 = listoneNot3;
        this.root1 = root1;
        this.root2 = root2;
        this.dirName1 = dirName1;
        this.dirName2 = dirName2;

        this.trFile1 = trFile1;
        this.StopIt = stop;
    }


    public void addFileLevelAfter(int currentID, Integer percentagesum1, int i) {

        File[] file = new File[1];
        File f0File = null;

        lock.lock();
        try {
            while (true) {
                trFile1.getFileInfo(file);
                f0File = file[0];
                if (!StopIt[0] && f0File == null) {
                    System.out.println("线程" + currentID + " 突然停止!");
                    break;
                }
                if (f0File != null) {
                    i++;
                    NumberFormat numberFormat = NumberFormat.getNumberInstance();
                    numberFormat.setMaximumFractionDigits(2);
                    String result = numberFormat.format((float) i / (float) percentagesum1 * 100);
                    FileComparesServiceImpl.percentage1 = result + "%";
                    FileComparesServiceImpl.syncCurrentName1 = f0File.getName();
                    System.out.println("当前为检测线程" + currentID + "读取的文件为第" + i + "个文件" + "文件名为" + f0File + "   文件总数为" + percentagesum1);

                    System.out.println("此时百分比为" + FileComparesServiceImpl.percentage1);


                    if (String.valueOf(f0File).startsWith(dirName1)) {
                        File file1 = new File(f0File.toString().replace(dirName1, dirName2));
                        if (file1.exists()) {
                            if (f0File.lastModified() != file1.lastModified()) {
                                Fc fc1 = new Fc();
                                fc1.setPath(f0File.getPath());
                                fc1.setLastmodified(String.valueOf(f0File.lastModified()));
                                fc1.setMd5(Md5.getMD5File(f0File));
                                listoneNot3.add(fc1);

                            }
                        } else {
                            Fc fc = new Fc();
                            fc.setPath(f0File.getPath());
                            fc.setLastmodified(String.valueOf(f0File.lastModified()));
                            fc.setMd5(Md5.getMD5File(f0File));

                            listoneNot1.add(fc);
                        }
                    } else {
                        File file2 = new File(f0File.toString().replace(dirName2, dirName1));
                        if (file2.exists()) {
                            if (f0File.lastModified() != file2.lastModified()) {
                                Fc fc1 = new Fc();
                                fc1.setPath(f0File.getPath());
                                fc1.setLastmodified(String.valueOf(f0File.lastModified()));
                                fc1.setMd5(Md5.getMD5File(f0File));
                                listoneNot3.add(fc1);

                            }
                        } else {
                            Fc fc = new Fc();
                            fc.setPath(f0File.getPath());
                            fc.setLastmodified(String.valueOf(f0File.lastModified()));
                            fc.setMd5(Md5.getMD5File(f0File));
                            listoneNot2.add(fc);
                        }
                    }
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("线程" + currentID + " 正常停止!");

        } finally {
            lock.unlock();
            latch.countDown();
        }

    }


}

控制台结果

检测线程应该是对比到某个文件(不过无伤大雅)
控制台部分日志

返回结果

在这里插入图片描述

进度条结果

在这里插入图片描述

个人总结

文件对比这个功能,我大概写了半个多月,
步骤大概就是
0.先沟通需求,看能不能降低需求难度(很重要)(比如文件对比这个最先的想法是做出类似于idea的文件对比,但是idea的文件对比是什么数量级,你要的什么数量级你不清楚吗?)
1.先有写的思路,无论这个思路是问别人的还是自己想的还是自己在某个博客里看的,重要的是有实现的思路。
2.写demo,思路试一下行不行得通。
3.优化思路,基本上就是,上多线程,调整多线程。
4.看一下功能运行的时间 是否满意 ,不满意重新1,2,3(从看文件比对这个专栏,也可以看出,我大概写了5-6个不同的解决思路,最初的方案是直接比对md5值 1k文件比对直接上100s,最后的结果是20w文件比对15s(普通笔记本))
5.最后就是,上项目

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java文件对比7,一个线程读一个线程写、返回给前端进度条数据 的相关文章

随机推荐

  • frp实现内网穿透

    文章目录 一 frp是什么 二 使用步骤 1 需要两台服务器 2 下载frp 和go语言 基于 1 通过自定义域名访问内网的 Web 服务 启动 windows下安装frpc ini 2 配置token才能访问 3 配置udp 4 通过 S
  • 字符数组与字符指针的区别

    字符数组与字符指针的区别 在 C 语言中 可以用两种方法表示和存放字符串 1 用字符数组存放一个字符串 char str IloveChina 2 用字符指针指向一个字符串 char str IloveChina 那么这两种表示方式有什么区
  • 内网渗透之信息收集

    一 内网信息收集概述 渗透测试人员进人内网后 面对的是一片 黑暗森林 所以 渗透测试人员首先需要对当前所处的网络环境进行判断 判断涉及如下三个方面 我是谁 一对 当前机器角色的判断 这是哪 一对 当前机器所处网络环境的拓扑结构进行分析和判断
  • Stm32最小系统板电路图设计、PCB设计

    目录 一 电路设计 1 复位电路 2 时钟电路 3 电源电路 4 SWD接口电路 5 BOOT启动电路 二 原理图绘制 1 工程的建立 2 原理图的绘制 2 1 使用已有库绘制原理图 2 2 构建原理图库 2 3 整体原理图 三 PCB绘制
  • Java堆和栈

    Java堆和栈是Java程序中两个重要的数据结构 它们在程序的运行过程中发挥着重要的作用 本文将介绍Java堆和栈的基本概念 区别 操作以及应用场景 帮助读者更好地理解和应用这两个数据结构 一 基本概念 Java堆 Heap 和栈 Stac
  • vue+elementui 登录页面

    vue elementui 登录页面 html代码
  • Windows 终端 Terminal 配置

    文章目录 Windows 终端 Terminal 配置 修改默认启动的命令 添加 cmder 到 Windows Terminal 添加 git bash 到 Windows Terminal 为Windows PowerShell 配置别
  • vue3.0+elementplus table动态添加column

  • 【Vuex】前后端分离Vue路由拦截器与登录cookie保存

    文章目录 1 Vuex 初探 1 1 vuex 介绍 1 2 store 的使用 2 localStorage使用 2 1 localStorage介绍 2 2 localStorage语法 3 路由钩子函数 导航守卫 3 1 导航守卫介绍
  • 固定资产预算怎么管理的

    在现代企业管理中 固定资产预算的管理是一项至关重要的任务 它不仅关系到企业的经济效益 更关系到企业的长远发展 那么 如何进行有效的固定资产预算管理呢 明确固定资产预算的目标和原则 我们需要明确固定资产预算的目标和原则 固定资产预算的目标应该
  • 利用谷歌的预训练模型实现目标检测object_detection_tutorial.ipynb

    环境准备 运行这个预训练的模型需要准备一些环境 首先需要下载谷歌的models master zip 地址在https github com Master Chen models 下载完成后我们需要的是research objection
  • Java基于微信小程序的一起考研学习平台

    第一章 简介 本文研究了基于微信小程序一起考研学习平台 通过该系统 用户可以主动的在线学习 下载资料 解决实际的问题 提高了效率 同时加强了用户之间的相互交流沟通 促进了信息化的发展 本文研究开发的小程序是学习并上传下载的小程序 开发完成后
  • steam成就解锁器_SAM解锁steam成就教程科普

    SAM全称为SteamAchievementManager 是一款一键解锁steam成就的工具 能够方便快捷的解锁steam游戏成就 图文使用教程 先登录steam 然后打开SAM读取你steam库存游戏 会出现这个界面 图1 登录stea
  • Nginx中if条件语句不支持proxy_set_header的解决方案

    转 Nginx中if条件语句不支持proxy set header的解决方案 CHEGVA 最近开发有个需求 需要通过一个域名加国家地区代码跳转到不同机房的服务 由于中间还要走一层ingress 需要设置下指定域名的头 ingress才能打
  • Node.js 安装与版本管理(nvm 的使用)

    安装 Node js Node js 诞生于 2009 年 5 月 截至今天 2022 年 3 月 26 号 的最新版本为 16 14 2 LTS 和 17 8 0 Current 可以去官网下载合适的版本 其中 LTS Long Term
  • 一)pytorch框架与环境搭建

    一 初识pytorch框架 为什么选择pytorch 活跃度高 逐渐形成完整的开发生态 资源多 动态图结构 运行速度较快 代码简洁 易于理解调试 简单 学习成本低 可能遇到的问题 深度学习框架太多要如何选择 开源代码很多家 但阅读修改起来非
  • WP7中,在后台c#代码中控制Grid

    有时候需要在后台代码中修改Grid布局 大概实现代码如下 代码很简单 如果在xmal中改过布局应该一看就明白 操作方法 创建一个默认页面 后台代码中加如下代码 public partial class Page3 PhoneApplicat
  • Global CORS configuration

    Global CORS configuration As an alternative to fine grained annotation based configuration you can also define some glob
  • 关于微信小程序:封装请求

    点这里 查看进阶版请求封装 拦截器 处理token过期之后的无感知登录 重新发起刚才未成功的请求 新建一个文件夹request 随便起 新建env js 2 1 在这里 配置接口地址的公共地址部分 方便后续引用 这里使用的接口呢都是自己模拟
  • java文件对比7,一个线程读一个线程写、返回给前端进度条数据

    java文件对比 controller Service Serviceimpl 读取文件多线程工具类 对比文件多线程工具类 控制台结果 返回结果 进度条结果 个人总结 这个其实写的是有点问题的 想的是一个线程读 多个线程对比 结果写的是一个