Java实现文件拷贝(单线程与多线程)

Java实现文件拷贝(单线程与多线程)

  • 将一个文件拷贝到另一个文件目录下,单线程实现涉及到文件IO的知识,下面是我写的源代码:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class SingleCopy {
    public static void main(String[] args) {
        long a=System.currentTimeMillis();//程序开始执行的时间
        RandomAccessFile srcFile=null;
        RandomAccessFile destFile=null;
        try {
            //定义原文件路径和目标文件路径
            srcFile=new RandomAccessFile("E:\\学习资料\\图论\\JavaSE高级\\视频\\字符流.mp4","r");
            destFile=new RandomAccessFile("F:\\FileCopy\\字符流.mp4","rw");
            long startIndex=0;//源文件中赋值操作开始坐标
            long endIndex= srcFile.length();//源文件中赋值操作结束坐标
            srcFile.seek(startIndex);//将源文件中的指针移到开始坐标
            destFile.seek(startIndex);//将目标文件的指针移到开始坐标
            int len=-1;
            byte[] bytes=new byte[1024];//1KB
            while((len= srcFile.read(bytes))!=-1){
                startIndex+=len;
                destFile.write(bytes,0,len);
                if(startIndex>=endIndex){
                    break;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(srcFile!=null||destFile!=null){
                try {
                    //关闭流
                    srcFile.close();
                    destFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //方便GC(垃圾回收机制)回收,防止内存泄漏
                srcFile=null;
                destFile=null;
            }
        }
        System.out.println("单线程拷贝所用时间"+(System.currentTimeMillis()-a)/1000+"秒");//程序执行总共花费时间
    }    
}

用RandomAccessFile(随机存取文件类)的原因:

  1. RandomAccessFile可以随机访问文件,类中有一个seek(long p)方法,可以移动文件中的指针,将文件记录指针定位到p位置,这样就可以从p位置进行操作。
  2. RandomAccessFile是一个双向流,既可以读也可以写。
  • 多线程实现:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;


class CopyTread extends Thread{
    private String src;
    private String dest;
    private Long startIndex;
    private Long endIndex;
    private CountDownLatch latch;

    public CopyTread(String src, String dest, long startIndex, long endIndex,CountDownLatch latch) {
       this.src=src;
       this.dest=dest;
       this.startIndex=startIndex;
       this.endIndex=endIndex;
       this.latch=latch;
    }
    @Override
    public void run() {
        RandomAccessFile srcFile = null;
        RandomAccessFile destFile = null;
        try {
            //创建原文件和目标文件的随机访问字节流
            srcFile = new RandomAccessFile(src, "r");
            destFile = new RandomAccessFile(dest, "rw");
            srcFile.seek(startIndex);//将源文件中的指针移到开始坐标
            destFile.seek(startIndex);//将目标文件的指针移到开始坐标
            int len = -1;
            byte[] bytes = new byte[1024*1024*10];//每次从源文件中读取10MB内容
            while ((len = srcFile.read(bytes)) != -1) {
                startIndex += len;
                destFile.write(bytes, 0, len);
                if (startIndex >= endIndex) {
                    break;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (srcFile != null || destFile != null) {
                try {
                    //关闭流
                    srcFile.close();
                    destFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //方便GC(垃圾回收机制)回收,防止内存泄漏
                srcFile = null;
                destFile = null;
            }
        }
        latch.countDown();
    }
}

public class TreadCopy {
    public static void main(String[] args) throws InterruptedException {
        long a = System.currentTimeMillis();
        //源文件目录
        String src="E:\\学习资料\\图论\\JavaSE高级\\视频\\字符流..mp4";
        //目标文件目录
        String dest="F:\\FileCopy\\字符流Copy.mp4";
        //分copyNum次拷贝,每次一个线程
        int copyNum=4;
        //主线程来分配每个线程操作的起始坐标和结束坐标
        File srcfile=new File(src);
        long  l= srcfile.length();//源文件的总长度
        long len=l/copyNum;//每次copy的长度
        CountDownLatch latch=new CountDownLatch(copyNum);//定义一个线程计时器
        for (int i = 0; i < copyNum-1; i++) {
            CopyTread copyTread=new CopyTread(src,dest,len*i,len*(i+1),latch);
            copyTread.start();
        }
        CopyTread copyTread=new CopyTread(src,dest,(copyNum-1)*len,l,latch);
        copyTread.start();
        latch.await();
        System.out.println("多线程拷贝所用时间" + (System.currentTimeMillis() - a) / 1000 + "秒");
    }
}
  1. 定义一个CountDownLatch是为了让所有的子线程执行完再执行主线程中latch.await()后的代码,防止主线程比子线程先执行完,打印的时间就不是所有线程执行完的时间。
  2. 子线程的while循环中的srcFile.read(bytes)可能会读到超过endIndex的数据并写入目标文件,因为每个线程的最后一次执行while循环可能此时endIndex-startIndex的数据量不能装满bytes的空间,这样bytes就会继续从endIndex后的数据中读,直到判断if(startIndex >= endIndex)跳出循环。但这个不用担心,下一个线程进来会从自己的startIndex开始复制数据,虽然可能重复复制上个线程的数据,但这个线程会把这些数据放到目标文件startIndex处,也就是将上次多复制的内容覆盖,所以不会影响结果。
  • 执行结果
    可以看到执行前目标文件夹中并无拷贝的文件
    在这里插入图片描述
  1. 单线程执行结果:
    在这里插入图片描述在这里插入图片描述
  2. 多线程执行结果:
    在这里插入图片描述
    在这里插入图片描述
    可能会出现单线程用时短,这是因为我的电脑只有一个cpu,cpu调度多个线程反而需要花费时间。
发布了19 篇原创文章 · 获赞 9 · 访问量 2213

猜你喜欢

转载自blog.csdn.net/weixin_44480874/article/details/99413688
今日推荐