什么是多线程?
多线程可以理解为下载的通道,一个线程就是一个文件的下载通道,多线程也就是同时开启好几个下载通道。当服务器提供下载服务时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配。不难理解,如果你线程多的话,那下载的越快。
多线程原理:
多线程下载的原理是这样的:通常服务器同时与多个用户连接,用户之间共享带宽。如果N个用户的优先级都相同,那么每个用户连接到该服务器上的实际带宽就是服务器带宽的N分之一。可以想象,如果用户数目较多,则每个用户只能占有可怜的一点带宽,下载将会是个漫长的过程。
如果你通过多个线程同时与服务器连接,那么你就可以榨取到较高的带宽了。例如原来有10个用户都通过单一线程与服务器相连,服务器的总带宽假设为56Kbps,则每个用户(每个线程)分到的带宽是5.6Kbps,即0.7K字节/秒。如果你同时打开两个线程与服务器连接,那么共有11个线程与服务器连接,而你获得的带宽将是56/11*2=10.2Kbps,约1.27K字节/秒,将近原来的两倍。你同时打开的线程越多,你所获取的带宽就越大(原来是这样,以后每次我都通过1K个线程连接:P)。当然,这种情况下占用的机器资源也越多。有些号称“疯狂下载”的下载工具甚至可以同时打开100个线程连接服务器。
多线程结构示意图:
代码实现,及思路解释:
线程类(服务器)
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class DownThread extends Thread{
public int id; //线程编号
public URL url; //文件下载的路径
public File file; //文件保存的路径
public int singlethreadNum; //每个下载的数据量
public DownThread(int id, URL url, File file, int singlethreadNum) {
super();
this.id = id;
this.url = url;
this.file = file;
this.singlethreadNum = singlethreadNum;
}
@Override
public void run() {
try {
RandomAccessFile raf=new RandomAccessFile(file, "rwd");
//分别计算每个下载的开始位置和结束位置
int start=id*singlethreadNum;
int end=(id+1)*singlethreadNum-1;
//通过传过来的URL 获取下载的连接
HttpURLConnection conn=(HttpURLConnection) url.openConnection();
//设置下载超时值
conn.setReadTimeout(5000);
//设置一般请求属性
conn.setRequestProperty("Range", "bytes="+start+"-"+end);
//局部请求 状态码为206
if(conn.getResponseCode()==206){
//随机写文件的时候从哪个位置开始写
raf.seek(start);//定位文件
//获取输入流
InputStream is=conn.getInputStream();
byte[] bytes=new byte[1024];
int len=0;
while((len=is.read(bytes, 0, bytes.length))!=-1){
raf.write(bytes, 0, len);
}
raf.close();
is.close();
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
客户端类:
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class TestDown {
public static void main(String[] args) {
//设置线程数
int threadnum=50;
//下载路径
String path="http://localhost:8080/web_project/images/s8.jpg";
//httpurlconnection
try {
//获取下载地址
URL url=new URL(path);
//通过下载地址打开连接
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
//设定请求方式
conn.setRequestMethod("GET");
//判断响应码是否为200 只有200的时候才会进入这个网页
if(conn.getResponseCode()==200){
//获取服务器中文件的大小
int length=conn.getContentLength();
//保存的文件路径
File f=new File("C:/s8.jpg");
//对随机的文件进行读写
RandomAccessFile raf=new RandomAccessFile(f,"rwd");
//设置下载文件大小
raf.setLength(length);
raf.close();
//每个线程下载的数据量
int singleThreadNum= length%threadnum==0 ? length/threadnum:length/threadnum+1;
for (int i = 0; i < threadnum; i++) {
new DownThread(i, url, f, singleThreadNum).start();
}
}else{
System.out.println("网页错误");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}