nio、bio、nio多线程实现文件拷贝

1、bio拷贝文件

    /**
     * bio 拷贝文件
     * @param mFile  资源文件
     * @param toFile 目的地
     * @throws Exception
     */
    private static void c2(File mFile, File toFile) throws Exception {
        FileInputStream inputStream = new FileInputStream(mFile);
        FileOutputStream outputStream = new FileOutputStream(toFile);

        byte[] bt = new byte[1024];

        int length = 0;
        while ((length = inputStream.read(bt)) != -1) {
            outputStream.write(bt, 0, length);
        }
        outputStream.flush();
        outputStream.close();
        inputStream.close();
    }

2、nio拷贝文件

    /**
     * nio 拷贝文件
     * @param mFile  资源文件
     * @param toFile 目的地
     * @throws Exception
     */
    private static void c1(File mFile, File toFile) {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;

        FileChannel inputChannel = null;
        FileChannel outChannel = null;

        try {
            fileInputStream = new FileInputStream(mFile);
            fileOutputStream = new FileOutputStream(toFile);
            
            //获取管道
            inputChannel = fileInputStream.getChannel();
            outChannel = fileOutputStream.getChannel();

            //开始拷贝文件
            //3个参数意思是: 从第0个位置开始、到inputChannel.size()位置结束、文件目标位置
            inputChannel.transferTo(0, inputChannel.size(), outChannel);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
                fileInputStream.close();
                inputChannel.close();
                outChannel.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

3、多线程拷贝文件

3.1 我们新建一个类,继承Thread用于处理数据
public class CopyThread extends Thread {
    private String sourcePath;
    private String putPath;
    private long start, end;

    private FileChannel outChannel;
    private FileChannel inChannel;

    private RandomAccessFile input;
    private RandomAccessFile output;
    
    /**
     * 多线程拷贝文件
     *
     * @param sourcePath 源文件路径
     * @param putPath    目标路径
     * @param start      从第几个位置开始写入
     * @param end        到那个位置结束
     */
    public CopyThread(String sourcePath, String putPath, long start, long end) {
        this.sourcePath = sourcePath;
        this.putPath = putPath;
        this.end = end;
        this.start = start;
    }

    @Override
    public void run() {
        try {
            long starttime = System.currentTimeMillis();

            input = new RandomAccessFile(sourcePath, "r");
            output = new RandomAccessFile(putPath, "rw");
            
            //这个很关键,定位文件指针的起始位置
            input.seek(start);
            output.seek(start);
            
            inChannel = input.getChannel();
            outChannel = output.getChannel();
          
            //拷贝文件 
            inChannel.transferTo(start, (end - start), outChannel);

            System.out.println(Thread.currentThread().getName() + "结束用时:" + (System.currentTimeMillis() - starttime));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (outChannel != null) {
                    outChannel.close();
                }
                if (inChannel != null) {
                    inChannel.close();
                }
                if (output != null) {
                    output.close();
                }
                if (input != null) {
                    input.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
写一个方法来测试多线程拷贝文件
/**
* 多线程拷贝文件
* @param mFile  资源文件
* @param toFile 目的地
* @throws Exception
*/
private static void copy(File mFile, File toFile) {
        
        //把一个文件分成几块,就是要开几个线程
        private static int COUNT_THREAD = 5;
        
        String inpath = mFile.getAbsolutePath();
        String outpath = toFile.getAbsolutePath();
        
        //文件大小
        long len = mFile.length();
        
        //计算一个线程 里面有多少数据需要拷贝
        long oneNum = len / COUNT_THREAD;
        
        //例如 假设文件大小是 11,  11/5 
        
        for (int i = 0; i < COUNT_THREAD - 1; i++) {
            //文件分块拷贝数据
            new CopyThread
                    (inpath, outpath,
                            oneNum * i, oneNum * (i + 1)).start();
        }
        //整除的时候不一定能除干净,所以还需要做一个取模运算
        new CopyThread(inpath, outpath,
                oneNum + (len % COUNT_THREAD), len).start();

        System.out.println("拷贝结束");
    }
到这里FileChannel多线程拷贝文件就已经完成了。但是并不完美,执行代码后会发现立马输出了 “拷贝结束” ,我想要的需求是当文件拷贝完成后才输出这句话,看3.2。

3.2 为了实现多线程文件拷贝同步,我们需要做一点小小点改变, 新建一个类,实现Runnable接口 ,构造参数比之前多了一个 CountDownLatch
public class CopyRunnable implements Runnable {

    private String sourcePath;
    private String putPath;
    private long start, end;

    private FileChannel outChannel;
    private FileChannel inChannel;

    private CountDownLatch downLatch;
    private RandomAccessFile input;
    private RandomAccessFile output;

    
    /**
     * 多线程拷贝文件(同步)
     *
     * @param sourcePath 源文件路径
     * @param putPath    目标路径
     * @param start      从第几个位置开始写入
     * @param end        到那个位置结束
     * @param downLatch  同步控制器
     */
    public CopyRunnable(String sourcePath, String putPath, long start, long end, CountDownLatch downLatch) {
        this.sourcePath = sourcePath;
        this.putPath = putPath;
        this.end = end;
        this.start = start;
        this.downLatch = downLatch;
    }


    @Override
    public void run() {
        try {
            input = new RandomAccessFile(sourcePath, "r");
            output = new RandomAccessFile(putPath, "rw");

            input.seek(start);
            output.seek(start);

            inChannel = input.getChannel();
            outChannel = output.getChannel();

            copy();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //每个线程结束 调用此方法来计数 减一
            downLatch.countDown();
            try {
                if (outChannel != null) {
                    outChannel.close();
                }
                if (inChannel != null) {
                    inChannel.close();
                }

                if (output != null) {
                    output.close();
                }
                if (input != null) {
                    input.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void copy() throws IOException {
        inChannel.transferTo(start, (end - start), outChannel);
    }
}

3.4 测试多线程拷贝文件
/**
* 多线程拷贝文件 同步
* @param mFile  资源文件
* @param toFile 目的地
* @throws Exception
*/
private static void copy(File mFile, File toFile) {
        
        private static int COUNT_THREAD = 5;
    
        String inpath = mFile.getAbsolutePath();
        String outpath = toFile.getAbsolutePath();

        long len = mFile.length();

        long oneNum = len / COUNT_THREAD;
        
        //我们这里使用了线程池 核心线程、最大线程数、等待时间 都可以自己的实际需要做修改
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(COUNT_THREAD, COUNT_THREAD, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        
        //线程同步工具,设置一个线程数
        CountDownLatch downLatch = new CountDownLatch(COUNT_THREAD);

        for (int i = 0; i < COUNT_THREAD - 1; i++) {
            CopyRunnable runnable = new CopyRunnable(inpath, outpath, oneNum * i, oneNum * (i + 1), downLatch);
            threadPoolExecutor.execute(runnable);
        }
        CopyRunnable runnable = new CopyRunnable(inpath, outpath, oneNum + (len % COUNT_THREAD), len, downLatch);
        threadPoolExecutor.execute(runnable);
        
        //线程池不再接受新的任务
        threadPoolExecutor.shutdown();

        System.out.println("等待文件拷贝结束");
        try {
            //等待,一直到downLatch中到数据为0
            downLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("文件拷贝结束了");
    }

你可能感兴趣的:(nio、bio、nio多线程实现文件拷贝)