美文网首页Java多线程android收藏汇总
Java多线程下载框架01:多线程的好处以及断点续传原理

Java多线程下载框架01:多线程的好处以及断点续传原理

作者: 程序员学园 | 来源:发表于2017-10-26 17:57 被阅读66次

    一、为什么要使用多线程,多线程真的能提高效率吗?

    • 1.1为什么要使用多线程

    多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

    因为单线程只会映射到一个CPU上,而多线程会映射到多个CPU上,多线程技术本质是多线程硬件化,所以也会加快程序的执行速度。现在的PC或者手机有很多都是多核的,如果只使用单一的线程去处理任务,资源得不到充分利用。

    • 1.2多线程能提高效率吗

    打个比方,比如修一个桥洞,有2种开工方法

    方案一、只在桥的一头挖,直至挖到桥的另一头,从而打通桥洞,这可以看成是单线程。
    方案二、在桥的两头挖,同时开工,最后在桥的中间接通,从而打通桥动,这感觉肯定比方案一快了很多,好比多线程。

    假设每挖5分钟,就需要清理一下挖出来的泥土。有一个小车在清理它们,工人只有一个。

    单线程的做法是: 挖5分钟。然后工人停止挖,小车清理石土的5分钟里,工人在等待。
    2个线程的做发是: 挖5分钟,小车来清理泥土。这5分钟里,工人在另一头挖。

    这个比喻至少能说明点问题:小车清理泥土,就相当于磁盘io等相对于cpu计算来说比较慢的操作。在cpu空闲的时候可以让其去做其它事情,达到充分利用的效果。

    • 1.3线程越多越好?
    image.png

    并不是线程越多性能越好,当线程超过一定数量的时候,线程的调度将会变成很大的开销,反而会让性能降低,所以要适当使用多线程,不能滥用。二者不是线性关系。

    计算机中一般来说只有一个CPU,也就是说只有一个工人。现在把修桥方案变动一下。
    方案一:只在山的一头挖,直至挖到山的另一头,从而打通隧道,这可以看成是单线程。

    方案二:在山的两头挖,同时开工,最后在山的中间接通,从而打通隧道,这感觉肯定比1快了很多,好比多线程。

    方案二虽然是在山的两头开挖,但是由于工作的人只有一个,所以只有让这个人在山的两头跑,挖一会这头再去挖另一头,来回跑是要花费额外时间的(好比线程的切换和调度)。

    再举二个例子:
    例子一:
    A单核单处理器,开一个线程跑循环输出10万条打印信息
    B开100个线程输出10万条打印信息。
    后者比前者慢,因为输出端是临界资源(临界资源:多道程序系统中存在许多进程,它们共享各种资源,然而有很多资源一次只能供一个进程使用。一次仅允许一个进程使用的资源称为临界资源。许多物理设备都属于临界资源,如输入机、打印机、磁带机等。),线程抢占的时间大,单线程则无需抢占。

    例子二:
    A网络服务器处理,每个请求开一个线程,请求的处理时间极短,迅速返回。
    B一次提交10万个请求,则有10万次线程创建和销毁对应于一个工作线程处理这10万条。请求后者比前者肯定快。

    二、为什么要使用断点续传

    在进行数据上传的时候可能是多线程操作,很多图像数据同时做上传或者单一的图像,如果图像比较多或者单一图像数据比较大,自然不希望失败一次或者暂停一次之后完全重传,有断点续传功能可以节省网络流量和节省用户时间,体验自然比你一次次的重传好很多。

    三、Java断点续传原理

    3.1什么是断点续传

    所谓断点续传,也就是要从文件已经下载的地方开始继续下载。在以前版本的 HTTP 协议是不支持断点的,HTTP/1.1 开始就支持了。一般断点下载时才用到 Range 和 Content-Range 实体头。下面会介绍HTTP版本的发展历程。

    3.2什么是Range?

    模拟http请求

    当用户在听一首歌的时候,如果听到一半(网络下载了一半),网络断掉了,用户需要继续听的时候,文件服务器不支持断点的话,则用户需要重新下载这个文件。而Range支持的话,客户端应该记录了之前已经读取的文件范围,网络恢复之后,则向服务器发送读取剩余Range的请求,服务端只需要发送客户端请求的那部分内容,而不用整个文件发送回客户端,以此节省网络带宽。

    3.3HTTP1.1规范的Range是怎样一个约定?

    如果Server支持Range,首先就要告诉客户端,咱支持Range,之后客户端才可能发起带Range的请求。这里套用唐僧的一句话,你不说我怎么知道呢。response.setHeader('Accept-Ranges', 'bytes');

    Server通过请求头中的Range: bytes=0-xxx来判断是否是做Range请求,如果这个值存在而且有效,则只发回请求的那部分文件内容,响应的状态码变成206,表示Partial Content,并设置Content-Range。如果无效,则返回416状态码,表明Request Range Not Satisfiable。如果不包含Range的请求头,则继续通过常规的方式响应。

    3.4应用场景

    假设你要开发一个多线程下载工具,你会自然的想到把文件分割成多个部分,比如4个部分,然后创建4个线程,每个线程负责下载一个部分,如果文件大小为403个byte,那么你的分割方式可以为:0-99 (前100个字节),100-199(第二个100字节),200-299(第三个100字节),300-402(最后103个字节)。

    分割完成,每个线程都明白自己的任务,比如线程3的任务是负责下载200-299这部分文件,现在的问题是:线程3发送一个什么样的请求报文,才能够保证只请求文件的200-299字节,而不会干扰其他线程的任务。这时,我们可以使用HTTP1.1的Range头。

    Range头域可以请求实体的一个或者多个子范围,Range的值为0表示第一个字节,也就是Range计算字节数是从0开始的:

    表示头500个字节:Range: bytes=0-499
    表示第二个500字节:Range: bytes=500-999 
    表示最后500个字节:Range: bytes=-500 
    表示500字节以后的范围:Range: bytes=500- 
    第一个和最后一个字节:Range: bytes=0-0,-1
    同时指定几个范围:Range: bytes=500-600,601-999  
    

    所以,线程3发送的请求报文必须有这一行:

    Range: bytes=200-299
    

    服务器接收到线程3的请求报文,发现这是一个带有Range头的GET请求,如果一切正常,服务器的响应报文会有下面这行:
    HTTP/1.1 206 OK

    表示处理请求成功,响应报文还有这一行
    Content-Range: bytes 200-299/403
    斜杠后面的403表示文件的大小

    3.5Http协议的发展历程

    HTTP协议到现在为止总共经历了3个版本的演化,第一个HTTP协议诞生于1989年3月。

    xml属性 描述
    HTTP/0.9 1991年
    HTTP/1.0 1992-1996年
    HTTP/1.1 1997-1999年
    HTTP/2.0 2012-2014年

    也就是HTTP/1.1 从1997-1999 年就应用了,所以现在基本上是支持断点续传的。

    3.6模拟Http请求插件推荐

    最后推荐一个模拟http请求的插件:HttpRequester,可以模拟Get/Post请求等,还可以添加Headers,Parameters参数,非常方便。

    模拟POST请求

    在上面“3.2什么是Range?”已经显示了使用该插件进行Get请求的截图。
    传送门:HttpRequester怎么安装和使用
    https://jingyan.baidu.com/article/7c6fb4280b6a4180642c900c.html

    本文公号地址,后续文章持续更新中,微信扫描下方二维码免费关注!点此查看全部最新文章


    我的博客
    我的简书
    我的GitHub,喜欢的话给个star吧

    相关文章

      网友评论

        本文标题:Java多线程下载框架01:多线程的好处以及断点续传原理

        本文链接:https://www.haomeiwen.com/subject/ipztpxtx.html