java 多线程下载器_Java多线程的下载器(1)

实现了一个基于Java多线程的下载器,可提供的功能有:

1. 对文件使用多线程下载,并显示每时刻的下载速度。

2. 对多个下载进行管理,包括线程调度,内存管理等。

一:单个文件下载的管理

1. 单文件下载类层次

首先简要介绍一下单个文件下载管理的类层次:

来一张图来表示。

1335619601_2296.jpg

为需要下载的文件创建一个Download类,Download负责管理该文件下载时的线程管理、文件管理、当前速度计算等操作。

根据线程的数目tNum,将该文件分为tNum段,每段为一个DownloadBlock。在实际下载的过程中,并不是一次把所有的东西下载完,而是每次下载固定size的一段Di。所以每个DownloadBlock又会分成n段。

为每个DownloadBlock申请一个线程DownloadThread。其主要作用就是每次下载一段Di,并将其写入到文件中。

2. 单文件下载

对于单个下载,步骤如下

连接资源服务器,获取资源信息,创建文件

切分资源,为每个线程分配固定的下载区域。

1)封装下载的属性

在建立下载之前,我们把每一次下载进行抽象封装。

首先把URL、目标文件等封装在一个DownloadConfig类中。

其中包含了4个属性:

private URL url; //文件下载地址

private File file; //下载文件保存目标文件

private int nthread; //下载该文件需要的线程数

private int priority; //该下载的优先级

2)连接资源服务器,获取资源信息,创建文件,并指定文件大小

length =config.getUrl().openConnection().getContentLength();

RandomAccessFilefile= new RandomAccessFile(config.getFile(), "rw"); //随机读取

file.setLength(length);

file.close();

3)切分资源,为每个线程分配固定的下载区域,并将当前的下载加入到队列中

int size = length /config.getNthread();for(int i = 0; i < config.getNthread(); i++){int start = i *size;intlen;if(i == config.getNthread() - 1)

len= length -start;else len =size;//并将当前的下载加入到下载队列中

addDownloadBlock(getDownloadBlock(start, len));

}

3)启动线程进行下载

下载的步骤如下:

1. 创建缓存,创建连接。设置获取资源数据的范围,创建文件,并设置写入位置

//创建缓存

byte[] b;if(block.getLength()

b= new byte[(int)block.getLength()];elseb= new byte[Constants.BYTES_READ];//创建连接。设置获取资源数据的范围,从startPos到endPos

URLConnection con = null;

con.setRequestProperty("Range", "bytes=" + block.getStart() + "-" + block.getStart()+block.getLength()-1);

RandomAccessFile file= new RandomAccessFile(block.getDownload().getConfig().getFile(), "rw");//创建RandomAccessFile

file.seek(block.getStart()); //从startPos开始写入

2. 如果当前block的length大于0,则从URL资源处获取固定大小的资源,并将其写入到文件中。

3 .更新block块的start,以及length,如果length大于0,继续进行2,否则则表示当前block已经下载完毕,退出该线程。

InputStream in =block.getDownload().getConfig().getUrl().openStream();intn;//对该block内的文件进行下载,

while(count

long newLength = (block.getLength() - count) / 2;long newStart = block.getStart() + block.getLength() -newLength;

DownloadBlock newBlock=block.getDownload().getDownloadBlock(newStart, newLength);

block.setLength(block.getLength()-newLength);

block.getDownload().addDownloadBlock(newBlock);

}//写入文件

n =in.read(b);if(n < 0){break;

}else if(count + n >block.getLength()){

file.write(b,0, (int)(block.getLength() -count));

count=block.getLength();

}else{

count+=n;

file.write(b,0, n);

}//set block count in download

if(n > 0){//统计每个block中已经下载的段的个数,用于计算当前下载的速度。

block.getDownload().setBlockCount(block.getStart(), count);

}

}

in.close();

file.close();

二 . 当前文件下载速度与进度计算

如第一个图所表示的,每个Block中又分为了很多的段D1、D2、…Dn,因此当为 了计算当前下载的速度,需要将下载的段D的数量统计出来,这里使用了一个ConcurrentHashMap来保存每个block已经下载完成的段D的数目。其中key为每个block的start值,而value为该block已经下载完的段 D。

在当前时刻,我们需要统计当前Download已经下载完成段D的数量,然后再和上一时刻的相比较,则可以得出当前的下载速度。具体代码见下:

class CheckSpeedTask extendsTimerTask{private static final Log log = LogFactory.getLog(CheckSpeedTask.class);privateDownload download;private ConcurrentHashMapblockCounts;private long speed = 0; //Byte/S

private long count = 0; //Total downloaded byte count

private long lastCount = 0;private long time = 0; //Check time

private long lastTime = 0;public CheckSpeedTask(Download download, long startTime, ConcurrentHashMapblockCounts){this.download =download;this.lastTime =startTime;this.blockCounts =blockCounts;

}

@Overridepublic voidrun() {try{

time=System.currentTimeMillis();

count= 0;//需要统计当前已经下载完成段D的数量。

for(longc : blockCounts.values()){

count+=c;

}

speed= (count -lastCount)/((time - lastTime)/1000);

log.debug(blockCounts.size()+ " threads are downloading " + download + ", cuttent is " + speed + "Byte/S, " + (count * 1.0)/download.getLength()*100 + "% downloaded");

download.setCount(count);

download.setSpeed(speed);

lastTime=time;

lastCount=count;

}catch(Exception e) {//TODO: handle exception

e.printStackTrace();

}

}

}

这样我们就可以在Thread类的run()函数中,计算当前下载的速度

while(activeThreads.size() > 0 || blockQueue.size() > 0){

Thread.sleep(1000);

checkSpeed();

}

原文:http://blog.csdn.net/zhzhl202/article/details/7521377

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值