【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】多線程下載的基本原理和用法_第1頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】多線程下載的基本原理和用法_第2頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】多線程下載的基本原理和用法_第3頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】多線程下載的基本原理和用法_第4頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】多線程下載的基本原理和用法_第5頁(yè)
已閱讀5頁(yè),還剩4頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】多線程下載的基本原理和用法

剛學(xué)了下多線程的下載,可能是初次接觸的原因吧,理解起來(lái)覺(jué)得稍微有點(diǎn)難。所以想寫(xiě)一篇博客來(lái)記錄下,加深自己理解的同時(shí),也希望能夠幫到一些剛接觸的小伙伴。由于涉及到網(wǎng)絡(luò)的傳輸,那么就會(huì)涉及到http協(xié)議。建議在讀本文之前您對(duì)http協(xié)議有一定的了解。

線程可以通俗的理解為下載的通道,一個(gè)線程就是文件下載的一個(gè)通道,多線程就是同時(shí)打開(kāi)了多個(gè)通道對(duì)文件進(jìn)行下載。當(dāng)服務(wù)器提供下載服務(wù)時(shí),用戶之間共享帶寬,在優(yōu)先級(jí)相同的情況下,總服務(wù)器會(huì)對(duì)總下載線程進(jìn)行平均分配。我們平時(shí)用的迅雷下載就是多線程下載。

線程可以通俗的理解為下載的通道,一個(gè)線程就是文件下載的一個(gè)通道,多線程就是同時(shí)打開(kāi)了多個(gè)通道對(duì)文件進(jìn)行下載。當(dāng)服務(wù)器提供下載服務(wù)時(shí),用戶之間共享帶寬,在優(yōu)先級(jí)相同的情況下,總服務(wù)器會(huì)對(duì)總下載線程進(jìn)行平均分配。我們平時(shí)用的迅雷下載就是多線程下載。1:獲取目標(biāo)文件的大?。╰otalSize)

按照常識(shí),我們?cè)谙螺d一個(gè)文件之前,通常情況下是要知道該文件的大小,這樣才好在本地留好足量的空間來(lái)存儲(chǔ),免得出現(xiàn)還未下載完,存儲(chǔ)空間就爆了的情況。為了方便代碼的演示,本文在本地tomcat服務(wù)器的webapps/ROOT目錄下新建一個(gè)test.txt的文件,里面存儲(chǔ)了0123456789這10字節(jié)的數(shù)據(jù)。

按照常識(shí),我們?cè)谙螺d一個(gè)文件之前,通常情況下是要知道該文件的大小,這樣才好在本地留好足量的空間來(lái)存儲(chǔ),免得出現(xiàn)還未下載完,存儲(chǔ)空間就爆了的情況。為了方便代碼的演示,本文在本地tomcat服務(wù)器的webapps/ROOT目錄下新建一個(gè)test.txt的文件,里面存儲(chǔ)了0123456789這10字節(jié)的數(shù)據(jù)。2:確定要開(kāi)啟幾個(gè)線程(threadCount)

需要的文件在服務(wù)器上,那我們要開(kāi)通幾個(gè)通道去下載呢?一般情況下這是由CPU去決定的,但是CPU開(kāi)啟線程的數(shù)目也是有限的,不是想開(kāi)幾個(gè)線程就開(kāi)幾個(gè)線程。所開(kāi)線程的最大數(shù)量=(CPU核數(shù)+1),例如你的CPU核數(shù)為4,那么電腦最多可以開(kāi)啟5條線程。為了方便代碼演示,本文的threadCount=3

需要的文件在服務(wù)器上,那我們要開(kāi)通幾個(gè)通道去下載呢?一般情況下這是由CPU去決定的,但是CPU開(kāi)啟線程的數(shù)目也是有限的,不是想開(kāi)幾個(gè)線程就開(kāi)幾個(gè)線程。所開(kāi)線程的最大數(shù)量=(CPU核數(shù)+1),例如你的CPU核數(shù)為4,那么電腦最多可以開(kāi)啟5條線程。為了方便代碼演示,本文的threadCount=33:計(jì)算平均每個(gè)線程需要下載多少個(gè)字節(jié)的數(shù)據(jù)(blockSize)

理想情況下多線程下載是按照平均分配原則的,即:?jiǎn)尉€程下載的字節(jié)數(shù)等于文件總大小除以開(kāi)啟的線程總條數(shù),當(dāng)不能整除時(shí),則最后開(kāi)啟的線程將剩余的字節(jié)一起下載。例如:本文中的totalSize=10,threadCount=3,則前兩個(gè)開(kāi)啟的線程下載3KB的數(shù)據(jù),第三個(gè)開(kāi)啟的線程需要下載(3+1)KB的數(shù)據(jù)。

理想情況下多線程下載是按照平均分配原則的,即:?jiǎn)尉€程下載的字節(jié)數(shù)等于文件總大小除以開(kāi)啟的線程總條數(shù),當(dāng)不能整除時(shí),則最后開(kāi)啟的線程將剩余的字節(jié)一起下載。例如:本文中的totalSize=10,threadCount=3,則前兩個(gè)開(kāi)啟的線程下載3KB的數(shù)據(jù),第三個(gè)開(kāi)啟的線程需要下載(3+1)KB的數(shù)據(jù)。4:計(jì)算各個(gè)線程要下載的字節(jié)范圍。

平時(shí)我們做項(xiàng)目講究分工明確,同理多線程下載也需要明確各個(gè)下載的字節(jié)范圍,這樣才能將文件高效、快速、準(zhǔn)確的下載下來(lái)。即在下載過(guò)程中,各個(gè)線程都要明確自己的開(kāi)始索引(startIndex)和結(jié)束索引(endIndex)。

平時(shí)我們做項(xiàng)目講究分工明確,同理多線程下載也需要明確各個(gè)下載的字節(jié)范圍,這樣才能將文件高效、快速、準(zhǔn)確的下載下來(lái)。即在下載過(guò)程中,各個(gè)線程都要明確自己的開(kāi)始索引(startIndex)和結(jié)束索引(endIndex)。從上圖我們可以總結(jié)出一個(gè)公式:startIndex=threadId乘以blockSize;endIndex=(threadId+1)乘以blockSize-1;如果是最后一條線程,那么結(jié)束索引為:endIndex=totalSize-1;5:使用for循環(huán)開(kāi)啟3個(gè)子線程//每次循環(huán)啟動(dòng)一條線程下載

for(intthreadId=0;threadId<3;threadId++){

/**

*計(jì)算各個(gè)線程要下載的字節(jié)范圍

*/

//開(kāi)始索引

intstartIndex=threadId*blockSize;

//結(jié)束索引

intendIndex=(threadId+1)*blockSize-1;

//如果是最后一條線程(因?yàn)樽詈笠粭l線程可能會(huì)長(zhǎng)一點(diǎn))

if(threadId==(threadCount-1)){

endIndex=totalSize-1;

}

/**

*啟動(dòng)子線程下載

*/

newDownloadThread(threadId,startIndex,endIndex).start();

}6:獲取各個(gè)線程的目標(biāo)文件的開(kāi)始索引和結(jié)束索引的范圍。

告訴服務(wù)器,只要目標(biāo)段的數(shù)據(jù),這樣就需要通過(guò)Http協(xié)議的請(qǐng)求頭去設(shè)置(range:bytes=0-499)

告訴服務(wù)器,只要目標(biāo)段的數(shù)據(jù),這樣就需要通過(guò)Http協(xié)議的請(qǐng)求頭去設(shè)置(range:bytes=0-499)connection.setRequestProperty("range","bytes="+startIndex+"-"+endIndex);7:使用RandomAccessFile隨機(jī)文件訪問(wèn)類。創(chuàng)建一個(gè)RandomAccessFile對(duì)象,將返回的字節(jié)流寫(xiě)到文件指定的范圍

此處有個(gè)注意事項(xiàng):讓RandomAccessFile對(duì)象寫(xiě)字節(jié)流之前,需要移動(dòng)RandomAccessFile對(duì)象到指定的位置開(kāi)始寫(xiě)。

此處有個(gè)注意事項(xiàng):讓RandomAccessFile對(duì)象寫(xiě)字節(jié)流之前,需要移動(dòng)RandomAccessFile對(duì)象到指定的位置開(kāi)始寫(xiě)。raf.seek(startIndex);以上就是多線程下載的大致步驟。代碼如下:packagecom.example;

importjava.io.InputStream;

importjava.io.RandomAccessFile;

import.HttpURLConnection;

import.URL;

publicclassDownloadTest{

privatestaticfinalStringpath="http://localhost:8080/test.txt";

publicstaticvoidmain(String[]args)throwsException{

/**

*1.獲取目標(biāo)文件的大小

*/

inttotalSize=newURL(path).openConnection().getContentLength();

System.out.println("目標(biāo)文件的總大小為:"+totalSize+"B");

/**

*2.確定開(kāi)啟幾個(gè)線程

*開(kāi)啟線程的總數(shù)=CPU核數(shù)+1;例如:CPU核數(shù)為4,則最多可開(kāi)啟5條線程

*/

intavailableProcessors=Runtime.getRuntime().availableProcessors();

System.out.println("CPU核數(shù)是:"+availableProcessors);

intthreadCount=3;

/**

*3.計(jì)算每個(gè)線程要下載多少個(gè)字節(jié)

*/

intblockSize=totalSize/threadCount;

//每次循環(huán)啟動(dòng)一條線程下載

for(intthreadId=0;threadId<3;threadId++){

/**

*4.計(jì)算各個(gè)線程要下載的字節(jié)范圍

*/

//開(kāi)始索引

intstartIndex=threadId*blockSize;

//結(jié)束索引

intendIndex=(threadId+1)*blockSize-1;

//如果是最后一條線程(因?yàn)樽詈笠粭l線程可能會(huì)長(zhǎng)一點(diǎn))

if(threadId==(threadCount-1)){

endIndex=totalSize-1;

}

/**

*5.啟動(dòng)子線程下載

*/

newDownloadThread(threadId,startIndex,endIndex).start();

}

}

//下載的線程類

privatestaticclassDownloadThreadextendsThread{

privateintthreadId;

privateintstartIndex;

privateintendIndex;

publicDownloadThread(intthreadId,intstartIndex,intendIndex){

super();

this.threadId=threadId;

this.startIndex=startIndex;

this.endIndex=endIndex;

}

@Override

publicvoidrun(){

System.out.println("第"+threadId+"條線程,下載索引:"+startIndex+"~"+endIndex);

//每條線程要去×××器拿取目標(biāo)段的數(shù)據(jù)

try{

//創(chuàng)建一個(gè)URL對(duì)象

URLurl=newURL(path);

//開(kāi)啟網(wǎng)絡(luò)連接

HttpURLConnectionconnection=(HttpURLConnection)url.openConnection();

//添加配置

connection.setConnectTimeout(5000);

/**

*6.獲取目標(biāo)文件的[startIndex,endIndex]范圍

*/

//告訴服務(wù)器,只要目標(biāo)段的數(shù)據(jù),這樣就需要通過(guò)Http協(xié)議的請(qǐng)求頭去設(shè)置(range:bytes=0-499)

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

connection.connect();

//獲取響應(yīng)碼,注意,由于服務(wù)器返回的是文件的一部分,因此響應(yīng)碼不是200,而是206

intresponseCode=connection.getResponseCode();

//判斷響應(yīng)碼的值是否為206

if(responseCode==206){

//拿到目標(biāo)段的數(shù)據(jù)

InputStreamis=connection.getInputStream();

/**

*7:創(chuàng)建一個(gè)RandomAccessFile對(duì)象,將返回的字節(jié)流寫(xiě)到文件指定的范圍

*/

//獲取文件的信息

StringfileName=getFileName(path);

//rw:表示創(chuàng)建的文件即可讀也可寫(xiě)。

RandomAccessFileraf=newRandomAccessFile("d:/"+fileName,"rw");

/**

*注意:讓raf寫(xiě)字節(jié)流之前,需要移動(dòng)raf到指定的位置開(kāi)始寫(xiě)

*/

raf.seek(startIndex);

//將字節(jié)流數(shù)據(jù)寫(xiě)到file文件中

byte[]buffer=newbyte[1024];

intlen=0;

while((len=is.read(buffer))!=-1){

raf.write(buffer,0,len);

}

//關(guān)閉資源

is.clos

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論