刚学了下多线程的下载,可能是初次接触的原因吧,理解起来觉得稍微有点难。所以想写一篇博客来记录下,加深自己理解的同时,也希望能够帮到一些刚接触的小伙伴。由于涉及到网络的传输,那么就会涉及到http协议。建议在读本文之前您对http协议有一定的了解。

线程可以通俗的理解为下载的通道,一个线程就是文件下载的一个通道,多线程就是同时打开了多个通道对文件进行下载。当服务器提供下载服务时,用户之间共享带宽,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。我们平时用的迅雷下载就是多线程下载。

多线程的下载大致可以分为如下几个步骤:

1: 获取目标文件的大小(totalSize)

按照常识,我们在下载一个文件之前,通常情况下是要知道该文件的大小,这样才好在本地留好足量的空间来存储,免得出现还未下载完,存储空间就爆了的情况。为了方便代码的演示,本文在本地tomcat服务器的webapps/ROOT目录下新建一个test.txt的文件,里面存储了0123456789这10字节的数据。

2: 确定要开启几个线程(threadCount)

需要的文件在服务器上,那我们要开通几个通道去下载呢?一般情况下这是由CPU去决定的,但是CPU开启线程的数目也是有限的,不是想开几个线程就开几个线程。所开线程的最大数量=(CPU核数+1),例如你的CPU核数为4,那么电脑最多可以开启5条线程。为了方便代码演示,本文的threadCount=3

3: 计算平均每个线程需要下载多少个字节的数据(blockSize)

理想情况下多线程下载是按照平均分配原则的,即:单线程下载的字节数等于文件总大小除以开启的线程总条数,当不能整除时,则最后开启的线程将剩余的字节一起下载。例如:本文中的totalSize=10,threadCount=3,则前两个开启的线程下载3KB的数据,第三个开启的线程需要下载(3+1)KB的数据。

4:计算各个线程要下载的字节范围。

平时我们做项目讲究分工明确,同理多线程下载也需要明确各个下载的字节范围,这样才能将文件高效、快速、准确的下载下来。即在下载过程中,各个线程都要明确自己的开始索引(startIndex)和结束索引(endIndex)。

从上图我们可以总结出一个公式:

startIndex = threadId乘以blockSize;

endIndex = (threadId+1)乘以blockSize-1;

如果是最后一条线程,那么结束索引为:

endIndex = totalSize - 1;

5: 使用for循环开启3个子线程

    //每次循环启动一条线程下载for(int threadId=0; threadId<3;threadId++){/*** 计算各个线程要下载的字节范围*///开始索引int startIndex = threadId * blockSize;//结束索引int endIndex = (threadId+1)* blockSize-1;//如果是最后一条线程(因为最后一条线程可能会长一点)if(threadId == (threadCount -1)){endIndex = totalSize -1;}/*** 启动子线程下载*/new DownloadThread(threadId,startIndex,endIndex).start();}

6:获取各个线程的目标文件的开始索引和结束索引的范围。

告诉服务器,只要目标段的数据,这样就需要通过Http协议的请求头去设置(range:bytes=0-499 )

connection.setRequestProperty("range", "bytes="+startIndex+"-"+endIndex);

7:使用RandomAccessFile随机文件访问类。创建一个RandomAccessFile对象,将返回的字节流写到文件指定的范围

此处有个注意事项:让RandomAccessFile对象写字节流之前,需要移动RandomAccessFile对象到指定的位置开始写。

    raf.seek(startIndex);

以上就是多线程下载的大致步骤。代码如下:

package com.example;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;public class DownloadTest {
private static final String path = "http://localhost:8080/test.txt";
public static void main(String[] args) throws Exception {/*** 1.获取目标文件的大小*/int totalSize = new URL(path).openConnection().getContentLength();System.out.println("目标文件的总大小为:"+totalSize+"B");/***2. 确定开启几个线程*开启线程的总数=CPU核数+1;例如:CPU核数为4,则最多可开启5条线程*/int availableProcessors = Runtime.getRuntime().availableProcessors();System.out.println("CPU核数是:"+availableProcessors);int threadCount = 3;/*** 3. 计算每个线程要下载多少个字节*/int blockSize = totalSize/threadCount;//每次循环启动一条线程下载for(int threadId=0; threadId<3;threadId++){/*** 4.计算各个线程要下载的字节范围*///开始索引int startIndex = threadId * blockSize;//结束索引int endIndex = (threadId+1)* blockSize-1;//如果是最后一条线程(因为最后一条线程可能会长一点)if(threadId == (threadCount -1)){endIndex = totalSize -1;}/*** 5.启动子线程下载*/new DownloadThread(threadId,startIndex,endIndex).start();}
}//下载的线程类
private static class DownloadThread extends Thread{private int threadId;private int startIndex;private int endIndex;public DownloadThread(int threadId, int startIndex, int endIndex) {super();this.threadId = threadId;this.startIndex = startIndex;this.endIndex = endIndex;}@Overridepublic void run(){System.out.println("第"+threadId+"条线程,下载索引:"+startIndex+"~"+endIndex);//每条线程要去找服务器拿取目标段的数据try {//创建一个URL对象URL url = new URL(path);//开启网络连接HttpURLConnection connection = (HttpURLConnection)url.openConnection();//添加配置connection.setConnectTimeout(5000);/*** 6.获取目标文件的[startIndex,endIndex]范围*///告诉服务器,只要目标段的数据,这样就需要通过Http协议的请求头去设置(range:bytes=0-499 )connection.setRequestProperty("range", "bytes="+startIndex+"-"+endIndex);connection.connect();//获取响应码,注意,由于服务器返回的是文件的一部分,因此响应码不是200,而是206int responseCode = connection.getResponseCode();//判断响应码的值是否为206if (responseCode == 206) {//拿到目标段的数据InputStream is = connection.getInputStream();/*** 7:创建一个RandomAccessFile对象,将返回的字节流写到文件指定的范围*///获取文件的信息String fileName = getFileName(path);//rw:表示创建的文件即可读也可写。RandomAccessFile raf = new RandomAccessFile("d:/"+fileName, "rw");/*** 注意:让raf写字节流之前,需要移动raf到指定的位置开始写*/raf.seek(startIndex);//将字节流数据写到file文件中byte[] buffer = new byte[1024];int len = 0;while((len=is.read(buffer))!=-1){raf.write(buffer, 0, len);}//关闭资源is.close();raf.close();System.out.println("第 "+ threadId +"条线程下载完成 !");} else {System.out.println("下载失败,响应码是:"+responseCode);}} catch (Exception e) {e.printStackTrace();}}
}//获取文件的名称
private static String getFileName(String path){//http://localhost:8080/test.txtint index = path.lastIndexOf("/");String fileName = path.substring(index+1);return fileName ;}
}

示例代码运行结果如下:

好了,本文写到此为止。以上是我个人对多线程下载的初步理解,如有不妥之处,还望大家多多指点,感谢!让我们共同学习,一起进步。

多线程下载的原理和基本用法相关推荐

  1. idm多线程下载的原理 idm下载线程数多少合适

    idm能够使我们的下载速度提升数倍以上,其核心在于多线程下载,今天聊聊idm多线程下载的原理,idm下载线程数多少合适,希望能够给各位提供一些帮助. 图1 idm优势 一.idm多线程下载的原理 1. ...

  2. 多线程系列之学习多线程下载的基本原理和基本用法(1)

    多线程下载在我们生活中非常常见,比如迅雷就是我们常用的多线程的下载工具,当然还有断点续传,断点续传我们在下一节来讲,android手机端下载文件时也可以用多线程下载,我们这里是在java中写一个测试, ...

  3. Android 多线程下载文件原理霸气解析介绍 -----文件的下载(3)

    1.首先我们先创建好下载的位置–根据url创建文件. /*** <p>Title: FlieStorageManager</p >* <p>Description: ...

  4. php文件断点续传,php文件下载限速,文件断点续传,多线程下载文件原理解析

    文件下载限速 首先,我们写一段使用php输出文件给浏览器下载的代码<?php /** * Created by PhpStorm. * User: tioncico * Date: 19-2-4 ...

  5. 文件多线程下载的原理与代码展示

    首先,我们要下载一个文件,可以通过多线程的方式快速下载!!! 多线程下载文件的步骤: 1.首先要知道请求下载的服务器支持断点下载,即支持request头信息中的Range的设置 2.然后通过对请求头设 ...

  6. Http多线程下载文件的处理机制

    本文出自博客Vander丶CSDN博客,如需转载请标明出处,尊重原创谢谢 博客地址:http://blog.csdn.net/l540675759/article/details/62111148 导 ...

  7. android 多线程下载,断点续传,线程池

    android 多线程下载,断点续传,线程池 你可以在这里看到这个demo的源码: https://github.com/onlynight/MultiThreadDownloader 效果图 这张效 ...

  8. 如何实现 HTTP 断点续传多线程下载

    1. HTTP断点续传多线程下载 一个比较常见的场景,就是断点续传/下载,在网络情况不好的时候,可以在断开连接以后,仅继续获取部分内容. 例如在网上下载软件,已经下载了 95% 了,此时网络断了,如果 ...

  9. Java swing 带界面和进度条的多线程下载器实现

    前言 因为大作业要做浏览器,浏览器要带下载器,所以现在先实现一个带界面的多线程下载器类. 多线程下载器原理 之前写过一个的[Java URLConnection类 实现多线程下载文件],只是那个dem ...

最新文章

  1. 如何有效使用每一点脑力总结_如何更有效地节省脑力和编码
  2. python读取字典元素笔记_python学习笔记:字典的使用示例详解
  3. 如何 循环 字母 php,PHP-php循环打印a-z字母的疑惑
  4. html5语句大全,html5基础语句(学习)
  5. spring更新后 外层事务查不到_再深一点:面试工作两不误,源码级理解Spring事务...
  6. 一个数据库管理员的自我救赎之路
  7. 双目视觉立体匹配算法
  8. 2019APMCM亚太数学建模题目
  9. 网站赚钱秘密--SEO的运用
  10. 顽固文件文件夹删除工具
  11. 改名后火速递表,飞天云动冲刺国内元宇宙第一股,借力能否成功?
  12. 记录一个找直线三等分点的方法及其证明
  13. 计算机网络总结篇-我们是怎么访问百度的?
  14. 小米路由器信号强度测试软件,内置天线给力 小米路由器信号实测
  15. 网页框架布局设计_实用的网页设计-框架和框架用法介绍
  16. python读取tsv文件_Python 读写 tsv
  17. 猜数字?我要王者荣耀
  18. 非常详细的SIP协议总结
  19. Oracle查询速度慢的原因总结
  20. Libevent库的简介

热门文章

  1. 互联网医疗的前世今生与未来
  2. 【网易云信 x 齿轮易创】互联网时代,如何在教育行业抢占C位
  3. 如何使用Java与Mysql进行数据交互
  4. vue适配移动端px自动转化为rem
  5. 不等双十一,ChemDraw降价活动已经打开!
  6. 预测分析:R语言实现2.6 特征选择
  7. 认识和入门 WebRTC
  8. 关于css3的自定义字体
  9. 如何选择开源许可证如何修改项目使其符合某种开源许可证
  10. 数据中心监控管理系统设计(之一)