一、為什么要使用多線程,多線程真的能提高效率嗎?
- 1.1為什么要使用多線程
多線程編程的目的,就是"最大限度地利用CPU資源",當某一線程的處理不需要占用CPU而只和I/O等資源打交道時,讓需要占用CPU資源的其它線程有機會獲得CPU資源。從根本上說,這就是多線程編程的最終目的。
因為單線程只會映射到一個CPU上,而多線程會映射到多個CPU上,多線程技術本質是多線程硬件化,所以也會加快程序的執(zhí)行速度?,F在的PC或者手機有很多都是多核的,如果只使用單一的線程去處理任務,資源得不到充分利用。
- 1.2多線程能提高效率嗎
打個比方,比如修一個橋洞,有2種開工方法

方案一、只在橋的一頭挖,直至挖到橋的另一頭,從而打通橋洞,這可以看成是單線程。
方案二、在橋的兩頭挖,同時開工,最后在橋的中間接通,從而打通橋動,這感覺肯定比方案一快了很多,好比多線程。
假設每挖5分鐘,就需要清理一下挖出來的泥土。有一個小車在清理它們,工人只有一個。
單線程的做法是: 挖5分鐘。然后工人停止挖,小車清理石土的5分鐘里,工人在等待。
2個線程的做發(fā)是: 挖5分鐘,小車來清理泥土。這5分鐘里,工人在另一頭挖。
這個比喻至少能說明點問題:小車清理泥土,就相當于磁盤io等相對于cpu計算來說比較慢的操作。在cpu空閑的時候可以讓其去做其它事情,達到充分利用的效果。
- 1.3線程越多越好?

并不是線程越多性能越好,當線程超過一定數量的時候,線程的調度將會變成很大的開銷,反而會讓性能降低,所以要適當使用多線程,不能濫用。二者不是線性關系。
計算機中一般來說只有一個CPU,也就是說只有一個工人?,F在把修橋方案變動一下。
方案一:只在山的一頭挖,直至挖到山的另一頭,從而打通隧道,這可以看成是單線程。
方案二:在山的兩頭挖,同時開工,最后在山的中間接通,從而打通隧道,這感覺肯定比1快了很多,好比多線程。
方案二雖然是在山的兩頭開挖,但是由于工作的人只有一個,所以只有讓這個人在山的兩頭跑,挖一會這頭再去挖另一頭,來回跑是要花費額外時間的(好比線程的切換和調度)。
再舉二個例子:
例子一:
A單核單處理器,開一個線程跑循環(huán)輸出10萬條打印信息
B開100個線程輸出10萬條打印信息。
后者比前者慢,因為輸出端是臨界資源(臨界資源:多道程序系統(tǒng)中存在許多進程,它們共享各種資源,然而有很多資源一次只能供一個進程使用。一次僅允許一個進程使用的資源稱為臨界資源。許多物理設備都屬于臨界資源,如輸入機、打印機、磁帶機等。),線程搶占的時間大,單線程則無需搶占。
例子二:
A網絡服務器處理,每個請求開一個線程,請求的處理時間極短,迅速返回。
B一次提交10萬個請求,則有10萬次線程創(chuàng)建和銷毀對應于一個工作線程處理這10萬條。請求后者比前者肯定快。
二、為什么要使用斷點續(xù)傳
在進行數據上傳的時候可能是多線程操作,很多圖像數據同時做上傳或者單一的圖像,如果圖像比較多或者單一圖像數據比較大,自然不希望失敗一次或者暫停一次之后完全重傳,有斷點續(xù)傳功能可以節(jié)省網絡流量和節(jié)省用戶時間,體驗自然比你一次次的重傳好很多。
三、Java斷點續(xù)傳原理
3.1什么是斷點續(xù)傳
所謂斷點續(xù)傳,也就是要從文件已經下載的地方開始繼續(xù)下載。在以前版本的 HTTP 協(xié)議是不支持斷點的,HTTP/1.1 開始就支持了。一般斷點下載時才用到 Range 和 Content-Range 實體頭。下面會介紹HTTP版本的發(fā)展歷程。
3.2什么是Range?

當用戶在聽一首歌的時候,如果聽到一半(網絡下載了一半),網絡斷掉了,用戶需要繼續(xù)聽的時候,文件服務器不支持斷點的話,則用戶需要重新下載這個文件。而Range支持的話,客戶端應該記錄了之前已經讀取的文件范圍,網絡恢復之后,則向服務器發(fā)送讀取剩余Range的請求,服務端只需要發(fā)送客戶端請求的那部分內容,而不用整個文件發(fā)送回客戶端,以此節(jié)省網絡帶寬。
3.3HTTP1.1規(guī)范的Range是怎樣一個約定?
如果Server支持Range,首先就要告訴客戶端,咱支持Range,之后客戶端才可能發(fā)起帶Range的請求。這里套用唐僧的一句話,你不說我怎么知道呢。response.setHeader('Accept-Ranges', 'bytes');
Server通過請求頭中的Range: bytes=0-xxx來判斷是否是做Range請求,如果這個值存在而且有效,則只發(fā)回請求的那部分文件內容,響應的狀態(tài)碼變成206,表示Partial Content,并設置Content-Range。如果無效,則返回416狀態(tài)碼,表明Request Range Not Satisfiable。如果不包含Range的請求頭,則繼續(xù)通過常規(guī)的方式響應。
3.4應用場景
假設你要開發(fā)一個多線程下載工具,你會自然的想到把文件分割成多個部分,比如4個部分,然后創(chuàng)建4個線程,每個線程負責下載一個部分,如果文件大小為403個byte,那么你的分割方式可以為:0-99 (前100個字節(jié)),100-199(第二個100字節(jié)),200-299(第三個100字節(jié)),300-402(最后103個字節(jié))。
分割完成,每個線程都明白自己的任務,比如線程3的任務是負責下載200-299這部分文件,現在的問題是:線程3發(fā)送一個什么樣的請求報文,才能夠保證只請求文件的200-299字節(jié),而不會干擾其他線程的任務。這時,我們可以使用HTTP1.1的Range頭。
Range頭域可以請求實體的一個或者多個子范圍,Range的值為0表示第一個字節(jié),也就是Range計算字節(jié)數是從0開始的:
表示頭500個字節(jié):Range: bytes=0-499
表示第二個500字節(jié):Range: bytes=500-999
表示最后500個字節(jié):Range: bytes=-500
表示500字節(jié)以后的范圍:Range: bytes=500-
第一個和最后一個字節(jié):Range: bytes=0-0,-1
同時指定幾個范圍:Range: bytes=500-600,601-999
所以,線程3發(fā)送的請求報文必須有這一行:
Range: bytes=200-299
服務器接收到線程3的請求報文,發(fā)現這是一個帶有Range頭的GET請求,如果一切正常,服務器的響應報文會有下面這行:
HTTP/1.1 206 OK
表示處理請求成功,響應報文還有這一行
Content-Range: bytes 200-299/403
斜杠后面的403表示文件的大小
3.5Http協(xié)議的發(fā)展歷程
HTTP協(xié)議到現在為止總共經歷了3個版本的演化,第一個HTTP協(xié)議誕生于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 年就應用了,所以現在基本上是支持斷點續(xù)傳的。
3.6模擬Http請求插件推薦
最后推薦一個模擬http請求的插件:HttpRequester,可以模擬Get/Post請求等,還可以添加Headers,Parameters參數,非常方便。

在上面“3.2什么是Range?”已經顯示了使用該插件進行Get請求的截圖。
傳送門:HttpRequester怎么安裝和使用
https://jingyan.baidu.com/article/7c6fb4280b6a4180642c900c.html。
本文公號地址,后續(xù)文章持續(xù)更新中,微信掃描下方二維碼免費關注!,點此查看全部最新文章

我的博客
我的簡書
我的GitHub,喜歡的話給個star吧