盒子
盒子
文章目录
  1. 原理
  2. 封装
  3. 分配线程
  4. 线程下载
  5. 总结

多线程下载

原理

多线程下载的原理就是将要下载的文件分成若干份,其中每份都使用一个单独的线程进行下载,这样对于文件的下载速度自然就提高了许多。

既然要分成若干部分分工下载,自然要知道各个线程自己要下载的起始位置,与要下载的大小。所以我们要解决线程的分配与各个线程定位到下载的位置。

封装

对于多线程下载我们可以将其封装到一个工具类中DownUtil,向其中传入下载的链接、文件存储路径、需要下载的线程数

1
2
3
4
5
6
public DownUtil(String path, String targetFile, int threadNum) {
this.path = path;
this.targetFile = targetFile;
this.threadNum = threadNum;
downThreads = new DownThread[threadNum];
}

其中DownThread实现的是各个线程的下载

分配线程

这里通过HttpURLConnection进行网络请求下载,通过getContentLength()方法获取下载文件的总大小,再对其平均分配各个线程需要下载的大小。这样就确定了下载的大小,下面就是定位到各个线程的开始位置进行下载,这里可以使用RandomAccessFile来追踪定位到要下载的位置,它的seek()方法可以进行定位。下面是详细代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void download() throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Connection", "Keep-Alive");

fileSize = conn.getContentLength();
conn.disconnect();
//需要下载的大小
int currentPartSize = fileSize / threadNum + 1;
RandomAccessFile file = new RandomAccessFile(targetFile, "rw");

//设置本地文件大小
file.setLength(fileSize);
file.close();
for (int i = 0; i < threadNum; i++) {
//下载开始位置
int startPos = i * currentPartSize;
RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw");
//定位到下载位置
currentPart.seek(startPos);
//下载线程
downThreads[i] = new DownThread(startPos, currentPartSize, currentPart);
downThreads[i].start();
}
}

线程下载

下面就是各个线程的下载DownThread,上面已经得到了各个线程要下载的初始位置,所以可以通过获取网络请求的输入流InputStream,通过skip()方法跳跃到指定位置进行读取数据,再写入到RandomAccessFile文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    public DownThread(int startPos, int currentPartSize, RandomAccessFile currentPart) {
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currentPart = currentPart;
}

@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Charset", "UTF-8");
InputStream in = conn.getInputStream();

skipFully(in, startPos);
byte[] buffer = new byte[1024];
int hasRead = 0;
while ((hasRead = in.read(buffer)) > 0 && length < currentPartSize) {
currentPart.write(buffer, 0, hasRead);
length += hasRead;
}
currentPart.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

这样就完成了一个简单的多线程的下载,最后调用封装类DownUtil就可以进行多线程下载。

总结

多线程的关键就是分配好需要下载的进程,定位进程下载的准确位置,获取输入流读取数据,同时写入到文件的相应位置。可以借助RandomAccessFile来进行定位。

当然也并非开的线程数越多下载的速度也就越快,因为线程越多对于程序处理这些线程也是一种负担,过多的话反而会降低下载的速度,所以要合理运用。

转载请指明出处 idisfkj博客:https://idisfkj.github.io

支持一下
赞赏是一门艺术