多线程加速下载:
【1】不是说线程开的越多下载就快(手机迅雷建议开3-4个线程就好)
【2】还受服务器带宽的影响
【3】更多的cpu资源给了你
多线程下载分析:
每个线程下载的计算公式:
JavaSE多线程下载简单实例:
package com.yueyue.haha;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class FileDownloader {
private static int THREADCOUNT = 3;
private static String path = "http://192.168.1.128:8080/pick.exe";
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
// [2]去服务器取数据 http://192.168.11.86:8080/news.xml
// [2.2]创建URL 对象指定我们要访问的 网址(路径)
URL url = new URL(path);
// [2.3]拿到httpurlconnection对象 用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// [2.4]设置发送get请求
conn.setRequestMethod("GET");// get要求大写 默认就是get请求
// [2.5]设置请求超时时间
conn.setConnectTimeout(5000);
// [2.6]获取服务器返回的状态码
int code = conn.getResponseCode();
// [2.7]如果code == 200 说明请求成功
if (code == 200) {
// [2.8]获得服务器返回的数据的长度
int length = conn.getContentLength();
System.out.println("数据的长度" + length);
//[2.9]创建与服务器返回的数据一模一样的数据申请空间
RandomAccessFile raf = new RandomAccessFile("sougou.exe", "rw");
raf.setLength(length);
// [3.0]将返回的数据进行分块来进行多线程下载
int blockSize = length / THREADCOUNT;
for (int i = 0; i < THREADCOUNT; i++) {
int startIndex = i * blockSize;
int endIndex = (i + 1) * blockSize;
if (i == THREADCOUNT - 1) {
endIndex = length - 1;
}
//[5.0]创建多线程对象,进行多线程下载
MuilteDownload muilteDownload = new MuilteDownload(startIndex, endIndex, i);
muilteDownload.start();
}
// [2.8]获取服务器返回的数据 是以流的形式返回的
}
} catch (Exception e) {
e.printStackTrace();
}
}
//[4.0]创建多线程的类,进行多线程下载
private static class MuilteDownload extends Thread {
private int startIndex;
private int endIndex;
private int threadId;
//[4.1]使用构造函数,把文件开始位置,结束位置以及多线程下载的id传进来
public MuilteDownload(int startIndex, int endIndex, int threadId) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@SuppressWarnings("resource")
@Override
public void run() {
try {
// [4.2]创建URL 对象指定我们要访问的 网址(路径)
URL url = new URL(path);
// [4.3]拿到httpurlconnection对象 用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// [4.4]设置发送get请求
conn.setRequestMethod("GET");// get要求大写 默认就是get请求
// [4.5]设置请求超时时间
conn.setConnectTimeout(5000);
//[4.6]设置一个请求Range,作用就是告诉服务器每个线程下载的开始位置以及结束位置
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
// [4.7]获取服务器返回的状态码
int code = conn.getResponseCode();
// [4.8]如果code == 200 说明请求成功 code==206代表部分请求资源成功
if (code == 206) {
//[4.9.0]创建随机读写对象
RandomAccessFile raf = new RandomAccessFile("sougou.exe", "rw");
raf.seek(startIndex);
//[4.9.1]获得服务器以流的形式进行返回的数据
InputStream in = conn.getInputStream();
//[4.9.2]向文件中写数据
int len=-1;
byte[] buffer = new byte[1024*1024];
while ((len=in.read(buffer))!=-1) {
raf.write(buffer, 0, len);
}
raf.close(); //关闭流,释放资源
System.out.println("threadId"+threadId+"在进行线程下载");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
javaSE断点续传实例:
实现断点续传:就是把当前线程下载的位置给存起来,下次再下载的时候就按照上次下载的位置继续下载就可以了
package com.yueyue.haha;
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 FileDownloader {
private static int THREADCOUNT = 3;// 假设开三个线程
private static String path = "http://192.168.1.230:8080/pick.exe";// 定义下载的路径
private static int runningThread;// 代表当前正在进行的线程
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
// [2]去服务器取数据 http://192.168.11.86:8080/news.xml
// [2.2]创建URL 对象指定我们要访问的 网址(路径)
URL url = new URL(path);
// [2.3]拿到httpurlconnection对象 用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// [2.4]设置发送get请求
conn.setRequestMethod("GET");// get要求大写 默认就是get请求
// [2.5]设置请求超时时间
conn.setConnectTimeout(5000);
// [2.6]获取服务器返回的状态码
int code = conn.getResponseCode();
// [2.7]如果code == 200 说明请求成功
if (code == 200) {
// [2.8]获得服务器返回的数据的长度
int length = conn.getContentLength();
System.out.println("数据的长度" + length);
// [2.9]创建与服务器返回的数据一模一样的数据申请空间
RandomAccessFile raf = new RandomAccessFile("sougou.exe", "rw");
raf.setLength(length);
// [2.9.1]把线程的数量赋值给当前正在进行的线程
runningThread = THREADCOUNT;
// [3.0]将返回的数据进行分块来进行多线程下载
int blockSize = length / THREADCOUNT;
for (int i = 0; i < THREADCOUNT; i++) {
int startIndex = i * blockSize;
int endIndex = (i + 1) * blockSize - 1;
if (i == THREADCOUNT - 1) {
endIndex = length - 1;
}
System.out.println("理论位置:" + "i:" + i + startIndex + "-" + endIndex);
// [5.0]创建多线程对象,进行多线程下载
MuilteDownload muilteDownload = new MuilteDownload(startIndex, endIndex, i);
muilteDownload.start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// [4.0]创建多线程的类,进行多线程下载
private static class MuilteDownload extends Thread {
private int startIndex;
private int endIndex;
private int threadId;
// [4.1]使用构造函数,把文件开始位置,结束位置以及多线程下载的id传进来
public MuilteDownload(int startIndex, int endIndex, int threadId) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
try {
// [4.2]创建URL 对象指定我们要访问的 网址(路径)
URL url = new URL(path);
// [4.3]拿到httpurlconnection对象 用于发送或者接收数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// [4.4]设置发送get请求
conn.setRequestMethod("GET");// get要求大写 默认就是get请求
// [4.5]设置请求超时时间
conn.setConnectTimeout(5000);
// [4.9.3]获得文件下载的位置
File file = new File(threadId + ".txt");
if (file.exists() && file.length() > 0) {// 用于判断是否存在以及为空
FileInputStream fis = new FileInputStream(file);
BufferedReader bffr = new BufferedReader(new InputStreamReader(fis));
String slastPostion = bffr.readLine();// 读出来就是上一次下载文件的位置
int lastPostion = Integer.parseInt(slastPostion);// 取得文件下载的位置
// 要改变startIndex的位置
startIndex = lastPostion + 1;
System.out.println("真实位置:" + "threadId:" + threadId + startIndex + "-" + endIndex);
}
// [4.6]设置一个请求Range,作用就是告诉服务器每个线程下载的开始位置以及结束位置
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
// [4.7]获取服务器返回的状态码
int code = conn.getResponseCode();
// [4.8]如果code == 200 说明请求成功 code==206代表部分请求资源成功
if (code == 206) {
// [4.9.0]创建随机读写对象
RandomAccessFile raf = new RandomAccessFile("sougou.exe", "rw");
raf.seek(startIndex);// 每个线程要从自己的位置开始写
// [4.9.1]获得服务器以流的形式进行返回的数据
InputStream in = conn.getInputStream();// 存的是sougou.exe
// [4.9.2]向文件中写数据
int len = -1;
int total = 0;
byte[] buffer = new byte[1024 * 1024 * 3];
while ((len = in.read(buffer)) != -1) {
raf.write(buffer, 0, len);
total += len;// 记录数据的下载长度
int currentPosition = startIndex + total;
// 想文件夹中写入文件下载的当前位置
RandomAccessFile rafile = new RandomAccessFile(threadId + ".txt", "rwd");
rafile.write(String.valueOf(currentPosition).getBytes());
rafile.close();// 关闭流,释放资源
}
raf.close(); // 关闭流,释放资源
System.out.println("threadId" + threadId + "线程下载完毕了");
// [6.0]把.txt文件删除,但具体哪个线程什么时候现在完毕,我们不知道
// 当线程下载完毕的时候,删除当前线程缓存的文件
runningThread--;
if (runningThread == 0) {
// 说明所有的文件下载完毕,把.txt文件删除
for (int i = 0; i < THREADCOUNT; i++) {
File deletefile = new File(i + ".txt");
deletefile.delete();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}