package com;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 多线程断点下砸
*
*1. 通过网络访问获取文件大小, 然后调用RandomAccess生成相同大小的空文件(如果文件存在就不需要生成)
*
*2. 生成三个线程同时定义一个集合来保存下载标记
*
*3. 当下载标记满足的时候,就表示加载完成
*
* 多线程断点下载 同时支持多个任务执行 ,设计上还是有问题的 , 比如应该将任务集体封装, 其内部实现三个线程进行下载.
*/
public class MutilDownLoadDemo {
private static volatile MutilDownLoadDemo demo;
/** 默认的线程数, 一个任务分配三个线程来完成, 比如 两个任务就需要 3 * 2 = 6 个线程来执行 */
private static final int DEFAULT_THREAD_COUNT = 3;
private int mThreadCount;
private ThreadPoolExecutor mExecutor;
public static MutilDownLoadDemo newInstance(){
if(demo == null){
synchronized (MutilDownLoadDemo.class) {
if(demo == null){
demo = new MutilDownLoadDemo(DEFAULT_THREAD_COUNT);
}
}
}
return demo;
}
private MutilDownLoadDemo(int threadCount){
this.mThreadCount = threadCount;
//初始化线程池
mExecutor = new ThreadPoolExecutor(mThreadCount, mThreadCount, 1,
TimeUnit.MINUTES, new LinkedBlockingDeque<Runnable>());
}
private synchronized void load(String path){
try {
//1. 获取文件的大小
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
int code = conn.getResponseCode();
if(code == 200){
long contentLength = conn.getContentLength();
System.out.println("contentLength:" + contentLength);
//2. 根据文件的大小来设置每个线程下砸的文件的数量
long block = contentLength / mThreadCount;
long remain = contentLength % mThreadCount;
System.out.println("block:" + block + " remain:" + remain);
//3. 定义一个锁,让三个线程同时完成之后,才能执行之后的
CyclicBarrier barrier = new CyclicBarrier(mThreadCount, new ClearCacheThread(path));
//4. 开启线程进行下载
for(int i = 0 ; i < mThreadCount ; i ++){
long start = i * block;
long end = (i + 1) * block - 1;
if(i == mThreadCount -1){end = contentLength;}
mExecutor.execute(new DownLoadThread(path, start, end, barrier, i));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
String url = "http://192.168.0.194:8080/a.zip";
MutilDownLoadDemo demo = MutilDownLoadDemo.newInstance();
demo.load(url);
String url1 = "http://192.168.0.194:8080/b.zip";
demo.load(url1);
}
/** 用来下载的线程类 */
public class DownLoadThread extends Thread{
private String path;
private long start;
private long end;
private CyclicBarrier mBarrier;
private int tag;
public DownLoadThread(String path ,long start, long end,CyclicBarrier barrier,int tag){
this.path = path;
this.start = start;
this.end = end;
this.mBarrier = barrier;
this.tag = tag;
}
@Override
public void run() {
try {
//需要判断下载文件是否存在,如果存在,已经下载了多少
long loadLen = buildTempFile(path,tag);
start += loadLen;
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setRequestProperty("Range", "bytes="+start+"-"+end);
System.out.println(Thread.currentThread().getName() + "/start:" + start + " end:" + end);
int code = conn.getResponseCode();
System.out.println(Thread.currentThread().getName() + "/code:" + code);
if(code == 206){
//使用RandomAccessFile一个字节一个字节的写
RandomAccessFile raf = new RandomAccessFile(new File(getFilePath(path)), "rwd");
raf.setLength(conn.getContentLengthLong());
raf.seek(start);
InputStream iStream = conn.getInputStream();
int len = -1;
byte[] buf = new byte[1024];
while((len = iStream.read(buf)) != -1){
raf.write(buf, 0, len);
loadLen += len;
RandomAccessFile tempRaf = new RandomAccessFile(getFilePath(path) + "_" + tag + ".txt", "rwd");
tempRaf.write(String.valueOf(loadLen).getBytes());
System.out.println(Thread.currentThread().getName() + "/loadLen:" + loadLen);
tempRaf.close();
}
raf.close();
iStream.close();
conn.disconnect();
mBarrier.await();
System.out.println(Thread.currentThread().getName() + "/ await");
}
} catch (MalformedURLException | InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private long buildTempFile(String path, int tag) {
String filePath = getFilePath(path) + "_" + tag + ".txt";
System.out.println("filePath:" + filePath);
File file = new File(filePath);
if(file.exists()){
try {
BufferedReader bufr = new BufferedReader(new FileReader(file));
String count = bufr.readLine();
bufr.close();
return Long.parseLong(count);
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}else{
try {
file.createNewFile();
BufferedWriter bufW = new BufferedWriter(new FileWriter(file));
bufW.write(String.valueOf(0));
bufW.close();
} catch (IOException e) {
e.printStackTrace();
}
return 0;
}
}
}
private String getFilePath(String path) {
return "d:\\download\\" + path.substring(path.lastIndexOf("/") + 1);
}
/** 下载完成之后清除相应的缓存数据 */
public class ClearCacheThread extends Thread{
private String path;
public ClearCacheThread(String path){
this.path = path;
}
@Override
public void run() {
System.out.println("下载完成-" + path);
}
}
}
java 多线程断点下载
最新推荐文章于 2024-03-10 23:38:03 发布