1. 为什么需要多线程下载?

    服务器的资源有限,同时的平均地分配给每个客户端。开启的线程越多抢占的服务的资源就越多,下载的速度就越块。

2. 下载速度的限制条件?

(1)你的电脑手机宽带的带宽。(网络运营商给用户的限制)

(2)服务器上传的带宽限制。 (服务器端资源获取速度的限制)----迅雷, p2p快播等下载,可以同时间使用多台服务器帮助用户下载资源,速度自然会加快。

注意:并不是开的线程越多下载速度越快,可能会消耗大量时间在线程调度上。

Android下推荐开启: ~ 线程。

3. 如何进行多线程的下载:

(1)在客户端本地创建一个空文件(申请一块内存),大小要和服务器上要下载的资源一样.

(2)开启3个线程,都去下载服务器的数据.

(3)当三个线程都工作完毕后,多线程的下载就结束了.

这里特别注意最后一个线程需要修正,主要是因为不可能实现完全等分,具体如下:

4.JavaSE代码实现多线程下载:

 (1)我们可以先编写java项目工程,调试实现多线程下载逻辑类MutilDownloader.java:

(2)打开Apache服务器,在相应的目录下存放测试下载文件,如下:

(3)MutilDownloader.java,如下:

 package com.himi.mutildownload;

 import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL; /**
* 多线程的下载器
*
*/
public class MutilDownloader {
/**
* Apache服务器上资源下载的路径
*/
private static final String path = "http://49.123.76.170/movies/test.avi";
/**
* 多少个线程去下载服务器的资源
*/
private static int threadCount = 4; /**
* 正在运行的线程的数量
*/
private static int runningThreadCount; public static void main(String[] args) throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
if (code == 200) {
int length = conn.getContentLength();
System.out.println("服务器文件的大小为:" + length); // 1. 创建一个空白文件文件的大小和服务器资源一样
RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rw");
raf.setLength(length);
raf.close(); // 每个线程下载的平均区块大小
int blocksize = length / threadCount;
System.out.println("每一份:" + blocksize); runningThreadCount = threadCount;
// 2. 开启3个线程,都去下载服务器的对应数据
for (int threadId = 0; threadId < threadCount; threadId++) {
int startIndex = threadId * blocksize;
int endIndex = (threadId + 1) * blocksize - 1; // 最后一个线程的修正,最后一个线程endIndex设置为文件末尾
if (threadId == (threadCount - 1)) {
endIndex = length - 1;// 文件byte是从0开始计数的
} new DownloadThread(startIndex, endIndex, threadId).start();
}
} // 3. 当三个线程都工作完毕后,多线程的下载就结束了. } public static class DownloadThread extends Thread {
/**
* 线程id
*/
int threadId; /**
* 当前线程下载的开始位置
*/
int startIndex;
/**
* 当前线程下载的结束位置
*/
int endIndex; /**
* 当前线程下载到文件的位置
*/
int filePosition; /**
*
* @param startIndex
* 开始位置
* @param endIndex
* 结束位置
* @param threadId
* 线程id
*/
public DownloadThread(int startIndex, int endIndex, int threadId) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
filePosition = startIndex;
} @Override
public void run() {
try {
// 用一个文本记录当前线程下载的进程
File file = new File(threadId + getFileName(path) + ".txt"); if (file.exists() && file.length() > 0) {
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
filePosition = Integer.parseInt(br.readLine());// 上一次下载到文件的哪个位子。
startIndex = filePosition;
fis.close();
} System.out.println("线程:" + threadId + "实际上下载的位置:" + startIndex + "~~~" + endIndex); URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
// 指定从服务器下载的范围,http请求的头
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); int code = conn.getResponseCode();// 2XX成功 3XX重定向 4XX资源找不到
// 5XX服务器异常
if (code == 206) {// 206:表示请求部分数据成功
// 返回服务器端对应数据的输入流
InputStream is = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rwd"); /**
* ☆☆☆☆☆非常重要☆☆☆☆☆ 一定要记得定位文件写的位置
* 不同线程在文件中(代码开头:创建的空文件)开始写入的位置是不一样的
*/
raf.seek(startIndex);
byte[] buffer = new byte[1024 * 1024 * 10];
int len = -1;
while ((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
filePosition += len;
RandomAccessFile rafinfo = new RandomAccessFile(file, "rwd");
rafinfo.write(String.valueOf(filePosition).getBytes());
rafinfo.close();
}
raf.close();
is.close(); System.out.println("线程:" + threadId + "下载完毕了。"); }
} catch (Exception e) {
e.printStackTrace();
} finally { // 三个线程都结束了,下载完毕
synchronized (MutilDownloader.class) {
runningThreadCount--;
if (runningThreadCount == 0) {
System.out.println("所有的线程都下载完毕了");
for (int i = 0; i < threadCount; i++) {
File f = new File(i + getFileName(path) + ".txt");
System.out.println(f.delete());
}
}
}
}
}
} /**
* 获取路径对应的文件名
*
* @param path
* @return
*/
private static String getFileName(String path) {
int beginIndex = path.lastIndexOf("/") + 1;
return path.substring(beginIndex);
} }

需要特别注意的是:RandomAccessFile.seek(startIndex),它是用来定位文件写入的位置。

运行程序,观察Console,如下:

刷新Java工程项目,如下:

双击打开test.avi,发现是可以播放的。

Android(java)学习笔记158:多线程断点下载的原理(JavaSE实现)的更多相关文章

  1. 我的Android进阶之旅------>Android基于HTTP协议的多线程断点下载器的实现

    一.首先写这篇文章之前,要了解实现该Android多线程断点下载器的几个知识点 1.多线程下载的原理,如下图所示 注意:由于Android移动设备和PC机的处理器还是不能相比,所以开辟的子线程建议不要 ...

  2. Android(java)学习笔记216:多线程断点下载的原理(Android实现)

    之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1.新建一个Android工程: (1)其中我们先实现布局 ...

  3. Android(java)学习笔记159:多线程断点下载的原理(Android实现)

    之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1. 新建一个Android工程: (1)其中我们先实现布 ...

  4. Android(java)学习笔记215:多线程断点下载的原理(JavaSE实现)

    1. 为什么需要多线程下载?     服务器的资源有限,同时的平均地分配给每个客户端.开启的线程越多抢占的服务的资源就越多,下载的速度就越块. 2. 下载速度的限制条件? (1)你的电脑手机宽带的带宽 ...

  5. 【原】Java学习笔记032 - 多线程

    package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...

  6. Java学习笔记之——多线程

    多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...

  7. Java学习笔记:多线程(一)

    Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...

  8. java学习笔记(5)多线程

    一.简介(过段时间再写,多线程难度有点大) --------------------------------------- 1.进程:运行时的概念,运行的应用程序 2.线程:应用程序内部并发执行的代码 ...

  9. Java 学习笔记(11)——多线程

    Java内部提供了针对多线程的支持,线程是CPU执行的最小单位,在多核CPU中使用多线程,能够做到多个任务并行执行,提高效率. 使用多线程的方法 创建Thread类的子类,并重写run方法,在需要启动 ...

随机推荐

  1. sublime text 3 安装vue 语法插件

    1.下载https://github.com/vuejs/vue-syntax-highlight,点击这里也可以下载压缩包 2.解压到C:\Users\***\AppData\Roaming\Sub ...

  2. 使用masonry手写约束

    在iOS开发过程中,手写contraints是非常痛苦的一件事情,往往那么一丢丢功能要写大量的代码,非常容易发生错误,并且非常不方便调试.所以只有在不得以的情况下才采用手工方式写contraints, ...

  3. Spring Data JPA 和MyBatis比较

    现在Dao持久层的解决方案中,大部分是采用Spring Data JPA或MyBatis解决方案,并且传统企业多用前者,互联网企业多用后者. Spring Data JPA 是Spring Data ...

  4. 用 SDL2 进行事件驱动编程

    其实没必要说得太复杂...就是读取用户输入啦. 沿用上一篇的代码,加入事件轮询. 环境:SDL2 + VC++2015 下面的代码将打开background.png和event.png,将backgr ...

  5. Get与Post的小知识

    Get与Post的小知识 一.传递参数: Get把参数包含在URL中,而在Post通过request body传递参数.因为参数直接暴露在URL上,GET比POST更不安全,所以不能用来传递敏感信息. ...

  6. Divide the Sequence

    Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission( ...

  7. CodeForces 712C Memory and De-Evolution (贪心+暴力)

    题意:现在有一个长度为 x 的正三角形,每次可以把一条边减小,然后用最少的时间变成长度为 y 的正三角形. 析:一开始,正着想,然后有一个问题,就是第一次减小多少才能最快呢?这个好像并不好确定,然后我 ...

  8. Vue 依赖收集原理分析

    此文已由作者吴维伟授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. Vue实例在初始化时,可以接受以下几类数据: 模板 初始化数据 传递给组件的属性值 computed wat ...

  9. hdoj1160【DP】

    现在还很弱,贴一个我bin的结题报告日后写到一定会了加油 说说感觉,读题不读好,然后读完想不出,知道是dp不好好想,先排序一列,再求另一列,dp[ i ]代表长度,那么需要输出整个序列需要路径和一个标 ...

  10. 洛谷P3825 [NOI2017]游戏(2-SAT)

    传送门 果然图论的题永远建图最麻烦……看着题解代码的建图过程真的很珂怕…… 先不考虑地图$x$,那么每一个地图都只能用两种赛车,于是我们可以用2-SAT来搞,用$i$表示这个地图能用的第一辆车,$i' ...