多线程复制文件,实现断点续传

想要的效果:把文件分成 n 份,开启 n 个线程,每个线程负责复制一份文件。当程序中断后,再次开启是不需要重新复制,而是从已经完成的地方开始复制。

思路

  • 单线程的断点续传,每一次复制前先获得目标文件已经复制的长度,根据已复制的长度设置偏移量开始复制。
  • 多线程的断点续传问题在于,已经复制的内容不是连续的,获取目标文件的长度获得的是总长度,不能获得每个线程单独已经复制的长度。
  • 解决方法: 用一个文本文档记录每个线程每一次复制的偏移量(获得偏移量等于获得已复制长度),下一次再复制时,直接读取该文档,获取该线程已经完成的偏移量,直接从该偏移量开始继续复制。

优化

  1. 根据上面思路,需要多一个记录文档的文件流,一开始打算用的是 ObjectOutputStream对象文件流,存储一个 Map 对象;或者是 BufferedReader 缓冲区字符流,存储 字符串,读写时再转换成普通的 Map 对象。

  2. 实际操作中,需要在一开始判断记录文档 不存在 或者 长度为0 时,创建一个文档,把 所有初始数据(index + index对应的偏移量) 先写入文档中。这样当多线程开始启动时,确保每个线程都能获得初始的偏移量。

  3. 可是这两种做法每次在读写时都需要进行转换数据格式,除此之外还会有 更新丢失 的问题:当线程A正在复制数据时,先获取了所有index的偏移量(包含线程B),此时线程B也在复制,复制完成后把新的偏移量写入文档,这时候线程A才复制完成,把偏移量写入文档,但是线程A的偏移量记录中记录的是线程B旧的偏移量,会把线程B刚刚新完成的偏移量覆盖了,导致 “复制失败(成功了但没完全成功)”

  4. 更新失败的解决方法有两个:

    • 添加 同步锁,多线程操作时,让每次只能有一个线程对记录文档进行读写操作。
    • 因为每次某个线程操作时,都会把文档中所有数据当成一个对象来处理,读取的时候没有问题,但是修改时,明明只是修改了只属于自己的一部分,却需要修改整个对象,即对所有线程数据进行操作。这是不合理的。所以需要 把文档中的数据拆成多份,每个线程只能获取并修改属于自己的数据,而且不会影响到其他的数据。
  5. 根据以上分析,决定使用 RandomAccessFile 随机访问流 来记录偏移量。RandomAccessFile 随机访问流 每次读写操作都可以 设置文件指针偏移 来进行,可以只读写部分数据,而不会影响到别的数据。由于文档是用来记录偏移量的,偏移量用的是 long 类型数据,一个 long 类型数据占8个字节。可以设置一个固定长度为 8 * 线程数的文档,每8个字节代表了一组偏移量数据。例如第2组数据是在第(2-1) * 8的偏移量开始记录,第5组数据是在第(5-1) * 8的偏移量开始记录.

最终代码

public static void main(String[] args) throws IOException {
   
   
    // 源文件
    File inputF = new File("E:\\read.mp4");
    // 目标文件
    File outputF = new File("E:\\write.mp4");
    // 记录偏移量文件
    File offsetF = new File("E:\\log.txt");
    // 线程数量
    int threadNum = 10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值