Android(java)学习笔记158:多线程断点下载的原理(JavaSE实现)
1. 為什么需要多線程下載?
? ? 服務(wù)器的資源有限,同時的平均地分配給每個客戶端。開啟的線程越多搶占的服務(wù)的資源就越多,下載的速度就越塊。
2. 下載速度的限制條件?
(1)你的電腦手機(jī)寬帶的帶寬。(網(wǎng)絡(luò)運(yùn)營商給用戶的限制)
(2)服務(wù)器上傳的帶寬限制。 (服務(wù)器端資源獲取速度的限制)----迅雷, p2p快播等下載,可以同時間使用多臺服務(wù)器幫助用戶下載資源,速度自然會加快。
注意:并不是開的線程越多下載速度越快,可能會消耗大量時間在線程調(diào)度上。
Android下推薦開啟:3 ~ 5線程。
3. 如何進(jìn)行多線程的下載:
(1)在客戶端本地創(chuàng)建一個空文件(申請一塊內(nèi)存),大小要和服務(wù)器上要下載的資源一樣.
(2)開啟3個線程,都去下載服務(wù)器的數(shù)據(jù).
(3)當(dāng)三個線程都工作完畢后,多線程的下載就結(jié)束了.
?
?這里特別注意最后一個線程需要修正,主要是因為不可能實現(xiàn)完全等分,具體如下:
?
4.JavaSE代碼實現(xiàn)多線程下載:
?(1)我們可以先編寫java項目工程,調(diào)試實現(xiàn)多線程下載邏輯類MutilDownloader.java:
?
?(2)打開Apache服務(wù)器,在相應(yīng)的目錄下存放測試下載文件,如下:
?
(3)MutilDownloader.java,如下:
1 package com.himi.mutildownload; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import java.io.InputStream; 7 import java.io.InputStreamReader; 8 import java.io.RandomAccessFile; 9 import java.net.HttpURLConnection; 10 import java.net.URL; 11 12 /** 13 * 多線程的下載器 14 * 15 */ 16 public class MutilDownloader { 17 /** 18 * Apache服務(wù)器上資源下載的路徑 19 */ 20 private static final String path = "http://49.123.76.170/movies/test.avi"; 21 /** 22 * 多少個線程去下載服務(wù)器的資源 23 */ 24 private static int threadCount = 4; 25 26 /** 27 * 正在運(yùn)行的線程的數(shù)量 28 */ 29 private static int runningThreadCount; 30 31 public static void main(String[] args) throws Exception { 32 URL url = new URL(path); 33 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 34 conn.setRequestMethod("GET"); 35 int code = conn.getResponseCode(); 36 if (code == 200) { 37 int length = conn.getContentLength(); 38 System.out.println("服務(wù)器文件的大小為:" + length); 39 40 // 1. 創(chuàng)建一個空白文件文件的大小和服務(wù)器資源一樣 41 RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rw"); 42 raf.setLength(length); 43 raf.close(); 44 45 // 每個線程下載的平均區(qū)塊大小 46 int blocksize = length / threadCount; 47 System.out.println("每一份:" + blocksize); 48 49 runningThreadCount = threadCount; 50 // 2. 開啟3個線程,都去下載服務(wù)器的對應(yīng)數(shù)據(jù) 51 for (int threadId = 0; threadId < threadCount; threadId++) { 52 int startIndex = threadId * blocksize; 53 int endIndex = (threadId + 1) * blocksize - 1; 54 55 // 最后一個線程的修正,最后一個線程endIndex設(shè)置為文件末尾 56 if (threadId == (threadCount - 1)) { 57 endIndex = length - 1;// 文件byte是從0開始計數(shù)的 58 } 59 60 new DownloadThread(startIndex, endIndex, threadId).start(); 61 } 62 } 63 64 // 3. 當(dāng)三個線程都工作完畢后,多線程的下載就結(jié)束了. 65 66 } 67 68 public static class DownloadThread extends Thread { 69 /** 70 * 線程id 71 */ 72 int threadId; 73 74 /** 75 * 當(dāng)前線程下載的開始位置 76 */ 77 int startIndex; 78 /** 79 * 當(dāng)前線程下載的結(jié)束位置 80 */ 81 int endIndex; 82 83 /** 84 * 當(dāng)前線程下載到文件的位置 85 */ 86 int filePosition; 87 88 /** 89 * 90 * @param startIndex 91 * 開始位置 92 * @param endIndex 93 * 結(jié)束位置 94 * @param threadId 95 * 線程id 96 */ 97 public DownloadThread(int startIndex, int endIndex, int threadId) { 98 this.startIndex = startIndex; 99 this.endIndex = endIndex; 100 this.threadId = threadId; 101 filePosition = startIndex; 102 } 103 104 @Override 105 public void run() { 106 try { 107 // 用一個文本記錄當(dāng)前線程下載的進(jìn)程 108 File file = new File(threadId + getFileName(path) + ".txt"); 109 110 if (file.exists() && file.length() > 0) { 111 FileInputStream fis = new FileInputStream(file); 112 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); 113 filePosition = Integer.parseInt(br.readLine());// 上一次下載到文件的哪個位子。 114 startIndex = filePosition; 115 fis.close(); 116 } 117 118 System.out.println("線程:" + threadId + "實際上下載的位置:" + startIndex + "~~~" + endIndex); 119 120 URL url = new URL(path); 121 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 122 conn.setRequestMethod("GET"); 123 // 指定從服務(wù)器下載的范圍,http請求的頭 124 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); 125 126 int code = conn.getResponseCode();// 2XX成功 3XX重定向 4XX資源找不到 127 // 5XX服務(wù)器異常 128 if (code == 206) {// 206:表示請求部分?jǐn)?shù)據(jù)成功 129 // 返回服務(wù)器端對應(yīng)數(shù)據(jù)的輸入流 130 InputStream is = conn.getInputStream(); 131 RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd"); 132 133 /** 134 * ☆☆☆☆☆非常重要☆☆☆☆☆ 一定要記得定位文件寫的位置 135 * 不同線程在文件中(代碼開頭:創(chuàng)建的空文件)開始寫入的位置是不一樣的 136 */ 137 raf.seek(startIndex); 138 byte[] buffer = new byte[1024 * 1024 * 10]; 139 int len = -1; 140 while ((len = is.read(buffer)) != -1) { 141 raf.write(buffer, 0, len); 142 filePosition += len; 143 RandomAccessFile rafinfo = new RandomAccessFile(file, "rwd"); 144 rafinfo.write(String.valueOf(filePosition).getBytes()); 145 rafinfo.close(); 146 } 147 raf.close(); 148 is.close(); 149 150 System.out.println("線程:" + threadId + "下載完畢了。"); 151 152 } 153 } catch (Exception e) { 154 e.printStackTrace(); 155 } finally { 156 157 // 三個線程都結(jié)束了,下載完畢 158 synchronized (MutilDownloader.class) { 159 runningThreadCount--; 160 if (runningThreadCount == 0) { 161 System.out.println("所有的線程都下載完畢了"); 162 for (int i = 0; i < threadCount; i++) { 163 File f = new File(i + getFileName(path) + ".txt"); 164 System.out.println(f.delete()); 165 } 166 } 167 } 168 } 169 } 170 } 171 172 /** 173 * 獲取路徑對應(yīng)的文件名 174 * 175 * @param path 176 * @return 177 */ 178 private static String getFileName(String path) { 179 int beginIndex = path.lastIndexOf("/") + 1; 180 return path.substring(beginIndex); 181 } 182 183 }需要特別注意的是:RandomAccessFile.seek(startIndex),它是用來定位文件寫入的位置。
?
運(yùn)行程序,觀察Console,如下:
刷新Java工程項目,如下:
?
雙擊打開test.avi,發(fā)現(xiàn)是可以播放的。
?
轉(zhuǎn)載于:https://www.cnblogs.com/hebao0514/p/4790941.html
總結(jié)
以上是生活随笔為你收集整理的Android(java)学习笔记158:多线程断点下载的原理(JavaSE实现)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: interface
- 下一篇: hdu1403 后缀数组