package com.thread.test;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MultiThThread implements Runnable
{
private String urlLocation;
private String filePath;
private long start;
private long end;
public MultiThThread(String urlLocation, String filePath, long start, long end) {
super();
this.urlLocation = urlLocation;
this.filePath = filePath;
this.start = start;
this.end = end;
}
/**
* 开启网络连接
* @return
* @throws Exception
*/
public HttpURLConnection getHttp() throws Exception
{
/**
* 创建url对象,中文名为统一资源定位符,
* 有时也被俗称为网页地址。
* 表示为互联网上的资源,如网页或者FTP地址。
*/
URL url = null;
if(urlLocation !=null)
{
url = new URL(urlLocation);
}
//开启网络连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//添加配置
conn.setReadTimeout(5000);
//传输方法
conn.setRequestMethod("GET");
return conn;
}
@Override
public void run()
{
// TODO Auto-generated method stub
try
{
//获取指定网页地址的资源
HttpURLConnection conn = getHttp();
/**
* 告诉服务器,只要目标段的数据,
* 这样就需要通过Http协议的请求头去设置(range:bytes=0至start-end )
*/
conn.setRequestProperty("Range", "bytes = "+"start"+"-"+"end");
File file = new File(filePath);
RandomAccessFile out = null;
if(file!=null)
{
/**
* 创建一个RandomAccessFile对象,写到文件指定的范围,
* rw表示文件即可读也可写
*/
out = new RandomAccessFile(file,"rw");
}
//让out写字节流之前,需要移动raf到指定的位置开始写
out.seek(start);
InputStream in = conn.getInputStream();
byte[] b = new byte[1024];
int len = 0;
while((len = in.read(b))>=0)
{
out.write(b);
}
in.close();
out.close();
}
catch (Exception e)
{
// TODO: handle exception
e.getMessage();
}
}
public static void main(String[] args)
{
Date startDate = new Date();
ThreadPool pool = new ThreadPool();
try
{
pool.getThreadPool("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3", "D:\\1.mp3", 100);
}
catch (Exception e)
{
// TODO: handle exception
e.printStackTrace();
}
System.out.println(new Date().getTime()-startDate.getTime());
}
}
class ThreadPool
{
public void getThreadPool(String urlLocation,String filePath,int poolLength) throws Exception
{
/**
* 创建一个可缓存线程池,如果线程池长度超过处理需要,
* 可灵活回收空闲线程,若无可回收,则新建线程。
*/
ExecutorService threadPool = Executors.newCachedThreadPool();
long len = getContentLength(urlLocation);
System.out.println(len);
for(int i=0;i<poolLength;i++)
{
//开始索引位置
long start = i*len/poolLength;
//结束索引位置
long end = (i+1)*len/poolLength-1;
/**
* 多线程下载最理想状态是:多条线程均分担
* 有时多条线程分担,不能均分。
* 当不能均分时,
* 剩余的多余下载任务需要最后一条线程承担
*/
if(i == poolLength-1)
{
end = len;
}
System.out.println(start+"--------------"+end);
MultiThThread downLoad = new MultiThThread(urlLocation, filePath, start, end);
threadPool.execute(downLoad);
}
threadPool.shutdown();
}
public static long getContentLength(String urlLocation) throws Exception
{
URL url = null;
if(urlLocation!=null)
{
url = new URL(urlLocation);
}
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
long len = conn.getContentLength();
return len;
}
}
多线程下载的大体几个步骤:
1:获取目标文件大小
2:开启线程池
3:统计每个线程需要下载的字节数
4:计算线程下载字节范围
5:获取各个线程的目标文件的开始索引和结束索引的范围。
6:使用RandomAccessFile随机文件访问类。创建一个RandomAccessFile对象,将返回的字节流写到文件指定的范围