Java利用多线程复制文件

2023-02-23 0 1,986

前言

复制一个文件,是学习IO流时最基本的操作。你可以使用字节型文件流,也可以使用高级缓冲流。
但是,它们都是单线程的。
如果需要复制一个大型文件,单线程的复制一般而言是不能够充分发挥CPU以及内存的性能。这时候就需要利用多线程来复制文件。
多线程的读:
我们很自然地想到,利用FileInputStream类的skip()方法,可以跳着读,这就对多线程比较友好,启动多个线程,第一个线程读一部分,第二个线程跳过一部分字节再读,这没有问题。
但是如果要写呢?我们知道FileOutputStream类是没有与skip类似的方法的,也就是说,它不能跳着写,这就很麻烦。
这就意味着,如果需要利用多线程复制一个文件,那么首先得把这个文件利用多线程并发,读取并同时写入成多个文件碎片,然后再利用单线程去一一读取这些文件碎片,把它们的内容写入到一个完整的文件中。必要的话,最后再删除这些中间文件碎片。
这个过程,复杂,且性能不高。涉及到两套读写,前面多线程读写,后面单线程读写。很显然这个过程虽然使用到了多线程但它不能提高性能,反而降低了性能。
既然鸡肋点在于FileOutputStream不能跳着写,那么就找一个能跳着写的类吧。
RandomAccessFile
RandomAccessFile是java Io体系中功能最丰富的文件内容访问类。即可以读取文件内容,也可以向文件中写入内容。但是和其他输入/输入流不同的是,程序可以直接跳到文件的任意位置来读写数据。
RandomAccessFile包含了以下两个方法来操作文件的记录指针:

  • long getFilePointer(); 返回文件记录指针的当前位置
  • void seek(long pos); 将文件记录指针定位到pos位置

有了RandomAccessFile这个类,上面的问题就迎刃而解,因为它最大的好处就是可以实现从指定位置写入一些数据到文件中!
顺便说一下它的构造方法:

  • RandomAccessFile(File file, String mode)
  • RandomAccessFile(String name, String mode)

mode表示RandomAccessFile的访问模式,她有4个值:

  • “r”:只有读的权限,如果你试图去执行写入方法,则抛出IOException异常。
  • “rw”:有读和写 两个权限,如果该文件不存在,则会创建该文件。
  • “rws”:相对于”rw” 模式,还要求对文件内容或元数据的每个更新都同步写入到底层设备。
  • “rwd”:相对于”rw” 模式,还要求对文件内容每个更新都同步写入到底层设备。

一般而言我们使用“r”和“rw”就够了,后面那两个我也不懂干嘛的。
代码
package testThread.file_threading;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

public class ThreadFileCopy extends Thread {
    private String srcFileStr;//源文件的路径
    private String desFileStr;//目标文件的路径 ,des --> destination目的地
    private long skipLen;//跳过多少个字节开始读/写
    private long workload;//总共要读/写多少个字节
    private final int IO_UNIT = 1024;//每次读写的基本单位(1024个字节)

    public ThreadFileCopy(String srcFileStr, String desFileStr, long skipLen, long workload) {
        this.srcFileStr = srcFileStr;
        this.desFileStr = desFileStr;
        this.skipLen = skipLen;
        this.workload = workload;
    }
    public void run(){
        FileInputStream fis = null;
        BufferedInputStream bis = null;//利用高级缓冲流,加快读的速度
        RandomAccessFile raf = null;
        try {
            fis = new FileInputStream(srcFileStr);
            bis = new BufferedInputStream(fis);
            raf = new RandomAccessFile(desFileStr,\"rw\");
            bis.skip(this.skipLen);//跳过一部分字节开始读
            raf.seek(this.skipLen);//跳过一部分字节开始写
            byte[] bytes = new byte[IO_UNIT];
            //根据总共需要复制的字节数 和 读写的基本单元 计算出一共需要读写的次数,利用读写次数控制循环
            long io_num = this.workload/IO_UNIT + 1;//因为workload/1024 很可能不能整除,会有余数
            if(this.workload % IO_UNIT == 0)
                io_num--;//如果碰巧整除,读写次数减一
            //count表示读取的有效字节数,虽然count不参与控制循环结束,
            // 但是它能有效避免最后一次读取出的byte数组中有大量空字节写入到文件中,导致复制出的文件稍稍变大
            int count = bis.read(bytes);
            while (io_num != 0){
                raf.write(bytes,0,count);
                count = bis.read(bytes,0,count);//重新计算count的值
                io_num--;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null)
                    bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (raf != null)
                    raf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

测试
package testThread.file_threading;

import java.io.File;

public class TestMain {
    public static void main(String[] args) {
        int thread_num = 5;//创建5个线程读写
        String srcFileStr = \"E:\\\\test\\\\123.flv\";
        String desFileStr = \"E:\\\\test\\\\thread.flv\";
        File srcFile = new File(srcFileStr);
        long workload = srcFile.length()/thread_num;//总共要读/写多少个字节
        //用一个数组来存储每个线程跳过的字节数
        long[] skipLenArr = new long[thread_num];
        for(int i = 0;i<skipLenArr.length;i++){
            skipLenArr[i] = i*workload;
        }
        //用一个数组来存储所有的线程
        ThreadFileCopy[] tfcs = new ThreadFileCopy[thread_num];
        //初始化所有线程
        for(int i = 0;i<tfcs.length;i++){
            tfcs[i] = new ThreadFileCopy(srcFileStr,desFileStr,skipLenArr[i],workload);
        }
        //让所有线程进入就绪状态
        for(int i = 0;i<tfcs.length;i++){
            tfcs[i].start();
        }
        System.out.println(\"复制完毕!\");
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

资源下载此资源下载价格为1小猪币,终身VIP免费,请先
由于本站资源来源于互联网,以研究交流为目的,所有仅供大家参考、学习,不存在任何商业目的与商业用途,如资源存在BUG以及其他任何问题,请自行解决,本站不提供技术服务! 由于资源为虚拟可复制性,下载后不予退积分和退款,谢谢您的支持!如遇到失效或错误的下载链接请联系客服QQ:442469558

:本文采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可, 转载请附上原文出处链接。
1、本站提供的源码不保证资源的完整性以及安全性,不附带任何技术服务!
2、本站提供的模板、软件工具等其他资源,均不包含技术服务,请大家谅解!
3、本站提供的资源仅供下载者参考学习,请勿用于任何商业用途,请24小时内删除!
4、如需商用,请购买正版,由于未及时购买正版发生的侵权行为,与本站无关。
5、本站部分资源存放于百度网盘或其他网盘中,请提前注册好百度网盘账号,下载安装百度网盘客户端或其他网盘客户端进行下载;
6、本站部分资源文件是经压缩后的,请下载后安装解压软件,推荐使用WinRAR和7-Zip解压软件。
7、如果本站提供的资源侵犯到了您的权益,请邮件联系: 442469558@qq.com 进行处理!

猪小侠源码-最新源码下载平台 PHP教程 Java利用多线程复制文件 http://www.20zxx.cn/586354/xuexijiaocheng/qes.html

猪小侠源码,优质资源分享网

常见问题
  • 本站所有资源版权均属于原作者所有,均只能用于参考学习,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担
查看详情
  • 最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,建议提前注册好百度网盘账号,使用百度网盘客户端下载
查看详情

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务