使用多線程下載文件能夠更快完成文件的下載,多線程下載文件之因此快,是由於其搶佔的服務器資源多。如:假設服務器同時最多服務100個用戶,在服務器中一條線程對應一個用戶,100條線程在計算機中並不是併發執行,而是由CPU劃分時間片輪流執行,若是A應用使用了99條線程下載文件,那麼至關於佔用了99個用戶的資源,假設一秒內CPU分配給每條線程的平均執行時間是10ms,A應用在服務器中一秒內就獲得了990ms的執行時間,而其餘應用在一秒內只有10ms的執行時間。就如同一個水龍頭,每秒出水量相等的狀況下,放990毫秒的水
確定比放10毫秒的水要多。
多線程下載的實現過程:
簡單瞭解一下多條線程將一條數據流分段下載的小思路:
假設10byte的數據流分3條線程下載:
10/3+1=4,即每條線程下載4個字節,0~3,4~7,8~9三段線程數據流(第三段理論值是4個字節,但因爲下載2個字節就結束數據下載內容了,因此數據流會自動關閉),那麼以下計算各個線程應該從什麼位置開始下載與在哪一個位置結束呢?
每條線程下載的數據長度:
10%3==0? 10/3 :10/3+1
即數據總長度%線程條數
計算線程開始位置公式:
線程的id(即第幾條線程,起始值爲0)* 每條線程下載的數據長度 = ?
計算線程結束位置公式:
線程的id(即第幾條線程,起始值爲0)+1 * 每條線程下載的數據長度-1 = ?
1>首先獲得下載文件的長度,而後設置本地文件
的長度。
HttpURLConnection.getContentLength();
RandomAccessFile file =
new RandomAccessFile(
"QQWubiSetup.exe",
"rwd");
file.setLength(filesize);//設置本地文件的長度
2>根據文件長度和線程數計算每條線程下載的數據長度和下載位置。如:文件的長度爲6M,線程數爲3,那麼,每條線程下載的數據長度爲2M,每條線程開始下載的位置如上圖所示。
3>使用Http的Range頭字段指定每條線程從文件的什麼位置開始下載,下載到什麼位置爲止,如:指定從文件的2M位置開始下載,下載到位置(4M-1byte)爲止,代碼以下:
HttpURLConnection.setRequestProperty(
"Range",
"bytes=2097152-4194303");
4>保存文件,使用RandomAccessFile類指定每條線程從本地文件的什麼位置開始寫入數據。
RandomAccessFile threadfile =
new RandomAccessFile(
"QQWubiSetup.exe ",
"rwd");
threadfile.seek(2097152);
//從文件的什麼位置開始寫入數據
核心代碼
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public
class ThreadDowmLoad {
public
static
void main(String[] args) {
String path =
"http://file1.51cto.com/index.php?aid=785088&k=205dfb403540e1ee182fd48c80247ab6&t=1347439680";
int threadNum = 3;
try {
new ThreadDowmLoad().downLoad(path, threadNum);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 從路徑中獲取下載文件名稱
*
* @param path下載文件的路徑
* @return 最後一個"/"後面字段的內容做爲下載後的文件名
*/
public static String getFileName(String path) {
// 得到最後一個"/"後面字段的內容做爲下載後的文件名
return path.substring(path.lastIndexOf("/") + 1);
}
/**
* @param path下載文件的路徑
* @param threadNum線程數
* @throws Exception
*/
public void downLoad(String path, int threadNum) throws Exception {
URL url = new URL(path);
HttpURLConnection httpURLConnection = (HttpURLConnection) url
.openConnection();
// 獲得httpURLConnection對象進行相關設置
// 設置請求方式
httpURLConnection.setRequestMethod("GET");
// 設置連接超時時間
httpURLConnection.setConnectTimeout(5 * 1000);
// 經過getContentLength()取得下載長度
int downloadLength = httpURLConnection.getContentLength();
// 生成同長度的本地文件,存放下載數據-----RandomAccessFile隨機文件訪問類
String fileName = getFileName(path);// 從路徑中獲取下載文件名稱
// 經過相對路徑將下載後的文件保存到根目錄中
File saveFile = new File(fileName);
// "rwd"表示同步將文件下載到指定路徑中,沒有緩存,若沒有「d」則會有緩存不一樣步
RandomAccessFile randomAccessFile = new RandomAccessFile(saveFile,
"rwd");
// 保存在本地的文件長度=網絡下載的文件長度
randomAccessFile.setLength(downloadLength);
randomAccessFile.close();
// 計算每條線程下載的數據長度
int block = downloadLength % threadNum == 0 ? (downloadLength / threadNum)
: (downloadLength / threadNum) + 1;
// 計算每條線程開始下載與結束下載的位置
for (int i = 0; i < threadNum; i++) {
// 開線程下載各自的數據段
new DownLoadThread(url, saveFile, block, threadNum).start();
}
}
/**
*
* @author YanJun URL url, 路徑 File saveFile,文件名 int block, 數據段長度 int
* threadNum 指定線程
*/
private final class DownLoadThread extends Thread {
private URL url;
private File saveFile;
private int block;
private int threadNum;
public DownLoadThread(URL url, File saveFile, int block, int threadNum) {
super();
this.url = url;
this.saveFile = saveFile;
this.block = block;
this.threadNum = threadNum;
}
@Override
public void run() {
// 計算下載文件數據的開始和結束位置
int startPosition = threadNum * block;
int endPosition = (threadNum + 1) * block - 1;
try {
// 隨機文件訪問類
RandomAccessFile randomAccessFile = new RandomAccessFile(
saveFile, "rwd");
// 從什麼位置訪問數據randomAccessFile.seek(startPosition);
randomAccessFile.seek(startPosition);
HttpURLConnection httpURLConnection = (HttpURLConnection) url
.openConnection();
// 獲得httpURLConnection對象進行相關設置
// 設置請求方式
httpURLConnection.setRequestMethod("GET");
// 設置連接超時時間
httpURLConnection.setConnectTimeout(5 * 1000);
// 使用Http的Range頭字段指定每條線程從文件的什麼位置開始下載,下載到什麼位置爲止,如:指定從文件的2M位置開始下載
httpURLConnection.setRequestProperty("Range", "bytes="
+ startPosition + "-" + endPosition);
// 取得輸入流
InputStream inputStream = httpURLConnection.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;// 當len=-1,表示流讀取完畢
// inputStream.read(buffer)--從輸入流裏讀取數據到緩衝區
while ((len = inputStream.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, len);
}
inputStream.close();
randomAccessFile.close();
System.out.println(threadNum + "號下載完成");
} catch (Exception e) {
// TODO Auto-generated catch block e.printStackTrace(); } } } }